1、背景介绍
在项目中借助POI 和 JXLS 两个开源工具jar实现excel数据导出,原有使用POI->HSSF方式进行数据导出,随着导出数据量的增大远远超出单sheet 65535条上限,将导出方式由POI->HSSF升级为POI->XSSF方式。
术语说明:
JXLS:国外经常使用数据模版导出工具,easyPOI 是国内经常使用数据模版导出工具,
POI->HSSF方式:支持Excel 97-2007版本的文件导出,单个sheet页最多能到导出65535条记录
POI->XSSF方式:支持Excel2007以上版本的文件导出,单个sheet无条数限制html
2、核心代码
maven jar包依赖apache
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schema</artifactId> <version>3.15</version> </dependency> <dependency> <groupId>net.sf.jxls</groupId> <artifactId>jxls-core</artifactId> <version>1.0.6</version> </dependency>
HSSF方式:api
import net.sf.jxls.transformer.XLSTransformer; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.util.CellRangeAddress; try{ //JXLS文件模版地址 文件配置详参:http://jxls.sourceforge.net/getting_started.html String outFilePath ="xxxx/file.xls"; //数据集合 Map<String, Object> dataMap = new HashMap<>(); //数据赋值 略 dataMap.put("users",new ArrayList<>()); //加载JXLS文件模版 InputStream fileStream = new FileInputStream(new File(outFilePath)); //对JXLS文件模版 进行数据赋值,数据来源有 XLSTransformer transformer = new XLSTransformer(); HSSFWorkbook workbook = (HSSFWorkbook) transformer.transformXLS( fileStream, dataMap); //获取 excel 文件的第一个sheet页 HSSFSheet sheet = workbook.getSheetAt(0); // 合并单元格操做 详参:https://poi.apache.org/apidocs/4.0/ // 将第一个sheet页中的 第4列 第1行到16行进行单元格合并(行和列从0开始) sheet.addMergedRegion(new CellRangeAddress(1, 17, 3, 3)); // 数据文件导出到指定目录 简化 FileOutputStream fout = new FileOutputStream("xxxx/exportData.xls"); workbook.write(fout); fout.close(); }catch (Throwable e){ //...... }
XSSF方式:app
import net.sf.jxls.transformer.XLSTransformer; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.ss.util.CellRangeAddress; try{ //JXLS文件模版地址 文件配置详参:http://jxls.sourceforge.net/getting_started.html String outFilePath ="xxxx/file.xlsx"; //.....同上 XSSFWorkbook workbook = (XSSFWorkbook) transformer.transformXLS( fileStream, dataMap); //获取 excel 文件的第一个sheet页 XSSFSheet sheet = workbook.getSheetAt(0); // 合并单元格操做 详参:https://poi.apache.org/apidocs/4.0/ // 将第一个sheet页中的 第4列 第1行到16行进行单元格合并(行和列从0开始) sheet.addMergedRegion(new CellRangeAddress(1, 17, 3, 3)); // 数据文件导出到指定目录 简化 FileOutputStream fout = new FileOutputStream("xxxx/exportData.xlsx"); workbook.write(fout); fout.close(); }catch (Throwable e){ //...... }
3、XSSF问题说明
正主来了,升级为POI->XSSF方式后,发现生成的excel文件,合并单元格列以下所示:
xss
发现合并后的列中的值并未清空,致使excel默认计数和求和数值不正确。经排查发现,由于POI->XSSF中进行addMergedRegion 合并单元格时并不支持“合并单元格时,仅保留左上角单元格的值,而放弃其余的值”。在excel2007版本在进行合并单元格时会默认提示以下所示:maven
排查了到POI 的4.1.0 版本为止,都不存在合并单元格时保留仅保留坐上角值的相关配置,因而技术上搞不定的事只能经过业务手段搞定,与业务沟通后退求其次,容许excel默认计数值错误,保证excel默认求和正确,具体操做方式以下所示:工具
try{ //.....同上略 XSSFWorkbook workbook = (XSSFWorkbook) transformer.transformXLS( fileStream, dataMap); //获取 excel 文件的第一个sheet页 XSSFSheet sheet = workbook.getSheetAt(0); //将2到16行 第3列的值设置为0 for(int i=2;i<17;i++){ sheet.getRow(i).getCell(3).setCellValue(0); } //.....同上略 }catch (Throwable e){ //...... }
最终经过手动将需合并的1~16行中第3列,保留第1行第3列的值,将第2行到第6行的第3列的值设置为0的方式保证导出的excel文件中默认求和数据的正确性。this
4、XSSF问题说明
1.IllegalArgumentException: Merged region A4 must contain 2 or more cells
缘由:因为CellRangeAddress 四个参数配置错误,计算公式为 (_lastRow - _firstRow + 1)*(_lastCol - _firstCol + 1)<2 ,经过计算公式算出可合并的行数小于2,说明没有可合并的列或行致使报错.net
2.org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF)
缘由:在使用POI->XSSF时,使用的FileInputStream 模版文件格式为file.xls致使问题产生,将模版另存为.xlsxexcel