做项目时需要使用easypoi导出excel,在以往数据量不多的时候都是直接用以下方案实现,并没有发现任何问题。
// 创建一个 XSSFWorkbook 对象,用于生成 .xlsx 文件
Workbook workbook = new XSSFWorkbook();
// 导出普通数据的sheet
for (Map<String, Object> map : list) {
List<ExcelExportEntity> entityList = (List<ExcelExportEntity>) map.get("entityList");
ExportParams entity = (ExportParams) map.get("entity");
Collection<?> dataSet = (Collection<?>) map.get("data");
new ExcelExportService().createSheetForMap(workbook, entity, entityList, dataSet);
//dataSet就是查询出来的要导出的全部数据,在方法之前已获取,一次性传入
}
但是在遇到数据量较大(超过10w)的Excel时,用上面的方案就会出现服务器内存和CPU飙高的问题,一开始怀疑是查询数据没做分页导致的,后来发现真正的问题是Workbook对象。第二天发现将Workbook改为SXSSWorkbook就不会飙高(查阅一些资料似乎是因为EasyPoi在数据大于10w时Workbook要使用SXSSWorkbook,但不确定),但仍然速度较慢,不能解决根本问题。
//大数据不能用XSSFWorkbook(大于10万),要用SXSSFWorkbook
Workbook workbook = new SXSSFWorkbook();
后来阅读EasyPoi源代码时发现,ExcelExportUtil的exportBigExcel方法就是提供给导出大数据的Excel设计的,遂改用该方法
//将每个sheet最多数据设置为500000,默认是100000,否则数据会分sheet
ExcelExportUtil.USE_SXSSF_LIMIT= 500000;
// 对查询结果进行分页处理
Workbook workbook = ExcelExportUtil.exportBigExcel(entity, excelList, (queryObject, num) -> {
//object就是你的查询方法中的查询条件,Page这里用了MybatisPlus的分页工具
//在这里实现循环分页查询逻辑
Page<Map<String, Object>> dataList = youMapper.selectDataByPage(queryObject, new Page(num, 50000, false));
// 如果查询结果为空,则返回null
if (dataList.getSize() == 0) {
return null;
}
// 将查询结果转化为List<Object>
List<Object> objects = new ArrayList<>(dataList.getRecords());
return objects;
}, queryObject);
用了这个方法后,内存和CPU飙高问题完全解决。
经验教训:遇到要用这种第三方工具解决问题时,建议多阅读工具的源代码,很多时候你以为难解决的问题别人早就帮你封装了一个方法。