插了一篇关于easypoi的,因为这两天刚好用到。Java项目可能会遇到生成凭证、报表报表等需求。使用easypoi的模板导出功能应该是一个不错的选择,因为easypoi简单易用,轻量级,学习成本低,容易上手。但是在使用过程中,遇到了如下问题(easypoi4.4.0):使用foreach后,生成的excel中模板中设置的公式丢失了。模板合并单元格的问题excel不能支持将两个结果集放在foreach的同一行。公式问题首先描述问题。设置模板非常简单。使用$fe设置foreach的输出结果集,然后总结出一定的输出:将模板保存为template1.xlsx,然后写一个简单的测试程序。为了方便调试和解决问题,测试程序工程没有在引入easypoi包的独立工程中做,而是直接从官网下载easypoi4.4.0的源码,并在下面新建一个测试类源代码项目。测试代码比较简单,就不贴完整代码了。数据准备好后,绑定模板,调用exportExcel生成excel文件,保存生成的excel文件。...TemplateExportParamstemplateExportParams=newTemplateExportParams("F:/template1.xlsx");Mapmap=newHashMap();map.put("用户列表",用户列表);地图。放(“用户列表2”,用户列表2);map.put("userList3",userList3);工作簿wk=ExcelExportUtil.exportExcel(templateExportParams,map);FileOutputStreamfo=newFileOutputStream("f:/test1.xlsx");//wk.setForceFormulaRecalculation(true);wk.write(fo);wk.close();然后打开生成的excel文件后,发现模板中设置的公式不见了:反复测试,发现是excel版模板的问题,如果xls没有问题,一个早期版本的excel,但是如果模板是xlsx,就有问题了。但是问题解决后才发现问题的原因。所以如果你也遇到类似的问题,可以选择使用xls格式的模板。当然,您也可以尝试查找原因,看看能否彻底解决问题。公式之所以消失,所以我们要发扬程序员顽固的精神,探究公式消失的原因。找到了ExcelExportOfTemplateUtil的addListDataToExcel方法,专门用来处理foreach(foreach是指模板中的fe、$fe等占位符,easypoi需要将此类占位符对应的数据集中的每一行数据写入到excel文件)。根据输入的地图数据集完成模板excel文件的移位后(即在目标excel文件中插入行,使dataset中的数据可以循环写入),调用PoiExcelTempUtil.reset改变sequenceaftertheinsertedrow移位后,恢复原来的模板内容。//固定是否有数据在后面,要进行插入操作if(isShift&&datas.size()>1&&datas.size()*rowspan>1&&cell.getRowIndex()+rowspan<=cell.getRow().getSheet().getLastRowNum()){intlastRowNum=cell.getRow().getSheet().getLastRowNum();intshiftRows=lastRowNum-cell.getRowIndex()-rowspan;cell.getRow().getSheet().shiftRows(cell.getRowIndex()+rowspan,lastRowNum,(datas.size()-1)*rowspan,true,true);mergedRegionHelper.shiftRows(cell.getSheet(),cell.getRowIndex()+rowspan,(datas.size()-1)*rowspan,shiftRows);templateSumHandler.shiftRows(cell.getRowIndex()+rowspan,(datas.size()-1)*rowspan);PoiExcelTempUtil.reset(cell.getSheet(),cell.getRowIndex()+rowspan+(datas.size()-1)*rowspan,cell.getRow().getSheet().getLastRowNum());}不知道reset的目的,试着把这行代码注释掉,对我的小测试好像没有什么影响,公式消失的问题确实解决了。但是我没有搞清楚他的具体作用,也不敢贸然评论不调用这个方法。那么,就看看这个方法吧。我不知道为什么,如果是FORMULA,我没有做任何处理。因此,添加了处理。为了显明一点,上图中注释掉的两句是新加的代码。那么无论是对于xls模板还是xlsx模板,都没有问题。模板合并单元格的问题是一定的意外,不容易复现。我们修改一下模板,如下图,只添加几个合并单元格:将数据调整为10行,运行得到的excel输出为:将数据调整为5行,得到:在某些情况下,template稍微复杂一点,如果输出数据的行数重合,会抛出异常:templateerror。所以我们需要分析原因。同样的,看ExcelExportOfTemplateUtil的addListDataToExcel方法。数据移位后,调用mergedRegionHelper的shiftRow方法:mergedRegionHelper这里我们需要简单分析一下mergedRegionHelper类。有一个注意事项:合并单元格助手类。它用于处理合并的单元格。对于我们的案例(根据模板导出),我们分析发现mergedRegionHelper其实主要是用来处理模板插入数据后的合并单元格。他的主要思路是:ExcelExportOfTemplateUtil#parseTemplate在解析模板时初始化mergedRegionHelper,mergedRegionHelper会读取模板当前sheet的所有合并单元格,缓存到属性mergedCache中。mergedCache的key值为sheet中的合并单元格:row_col,value为合并行数和列数组成的数组。在模板中为foreach插入行(shift)后,调用mergedRegionHelper的shiftRow方法处理mergedCache中缓存的数据。然后将数据集中的数据填入新插入的行中。在写入每个单元格之前,先判断该单元格是否为合并单元格,然后合并当前正在处理的单元格。判断是否为合并单元格的依据:判断原模板中对应的单元格是否为合并单元格是通过mergedRegionHelper的isMergedRegion方法来判断的,该方法实际上是检查mergedCache中是否存在当前单元格的缓存数据。不知道上面的描述是否清楚,下面结合我们上面的测试用例简单说明一下。比如我们模板中的第4行第3列第4列是合并单元格,mergedCache中的记录大概是[4_3,[1,2]],也就是说第4行第3列是合并单元格,合并1行,2列(为了便于理解,不考虑起始列从0开始的情况)。假设数据集包含5条数据,那么需要在Excel中第2行下方插入4行(原模板第2行已经写入了1条数据,所以不需要插入行).插入后,原来的第4行变成了第8行,所以mergedCache缓存的数据是错误的。mergedRegionHelper的shiftRow方法就是在插入行后调整这个错误。但是,上图中的调整算法存在问题。比如当第4行变成第8行时,假设模板的第8行已经有合并单元格,那么原来第8行的数据可能会被覆盖。风险,虽然代码已经处理了这种情况,但是没有处理好,还是会出问题。设想的解决方案之一是:如果能将mergedCache中的key按照行数进行倒序排序,mergedCache中的数据从Excel表格的最底行开始从下往上逐行处理,问题应该彻底解决了。但实际上,缓存后的这种缓存数据在使用过程中最好不要移动。大家可以想想有没有其他的方法可以解决shift带来的变化。于是想到的第二个解决方案是:shifting数据的时候不要重新处理mergedCache的数据,只让mergedRegionHelper记录sheet中当前插入(shift)了多少行,并考虑这个insertedrow在哪里需要使用mergedCache来缓存数据算一下。采用第二种方案需要特别注意转换的影响,不仅要仔细检查mergedRegionHelper,还要仔细检查mergedRegionHelper的调用者。比如在同一行输出两个结果集的场景,第二种方案不适合,需要考虑第一种方案。mergedRegionHelper比较简单,但是它的调用者可能比较复杂,主要包括:ExcelToHtmlServiceExcelExportOfTemplateUtil我的项目暂时只用到ExcelExportOfTemplateUtil,所以我们暂时只分析ExcelExportOfTemplateUtil。解决合并单元格问题#mergedRegionHelper转换首先需要添加成员变量,初始化为0:privateintrowsShifted=0;methodshiftrow,修改为简单粗暴,只累加插入的行数:methodgetRowAndColSpan:methodisMergedRegion:methodisNeedCreate:modified。解决单元格合并问题#ExcelExportOfTemplateUtiltransformation其实按理说我们mergedRegionHelper的transform方法应该不会影响调用者,但是测试发现还是有影响的。主要影响是setForeachRowCellValue方法://MergecorrespondingcellsbooleanisNeedMerge=(params.getRowspan()!=1||params.getColspan()!=1)&&!mergedRegionHelper.isMergedRegion(row.getRowNum()+1,ci);这里要判断新插入的行的每个单元格是否为合并单元格,如果是则合并。判断的依据是模板中设置了foreach(params已经处理)的行对应的cell是否为合并cell。但是源码中除了判断params外,还必须是!mergedRegionHelper.isMergedRegion(参数为当前cell)。直观的理解应该是:模板中对应的cell是合并后的cell,当前cell不在mergedRegionHelper的缓存中。我不太明白添加!mergedRegionHelper.isMergedRegion的原因。推测可能的逻辑是在单元格合并前判断一定是新插入的行,而不是模板中原来的行。我们修改完mergedRegionHelper之后,这个地方的判断就要调整一下:把图片中红框栏的部分注释掉就行了。在各种情况下重置合并单元格的模板(当然是非常有限的。。。),包括项目中出现问题报错的模板,再继续测试修改,都可以正常工作起来。当然,easypoi要应对各种场景。目前,以上解决方案可能只能解决我的项目和我的模板遇到的问题。如果情况比较复杂,上面的方案可能还是有问题。支持将两个结果集放在excel中的同一行中。不知道其他人有没有遇到过这个问题。比如我想用下面的模板导出:第一行左边3列是结果集userList结果集的数据,右边3列是结果集userList2的数据。比如userList设置了2条数据,userList2设置了3条数据。开始测试后,系统报错:实现这个目标还是有点难度。还需要修改ExcelExportOfTemplateUtil的addListDataToExcel方法,比较复杂:但修改后还是可以满足要求的:这种情况下,如果模板比较复杂,可能会出现问题,需要予以详细分析和解决。多于。PreviousQuartz-MisfireNexteasypoitemplateexportforeachsinglerowmultipleresultsets+mergedcells问题