(记)Excel导出优化之路
经过了几个月的迭代,项目上的导出组件逐渐完善,想以本篇文章来记录项目上的导出组件的优化过程。
Excel导出组件是一个注解驱动的spring-boot-starter导出组件,利用Spring AOP拦截controller中的GET方法的查询API,并通过注解声明在DTO上来表示数据与excel中的映射关系和单元格属性,依赖于Apache POI完成excel数据填充的工作。
- 与easyexcel比较
easyexcel提供了非常简单方便的导出api,支持注解驱动的导出配置,以及excel的读取和写入api,且声称在内存占用上消耗非常少。
Excel导出组件支持注解驱动的导出配置,而同时也提供了相对灵活的低层api,另外,Excel导出组件还支持异步,并封装了HTTP下载逻辑,在使用时无需关心文件下载的流操作。
Excel导出组件的API在单一需求的导出场景下更具优势(基于数据库查询的导出场景)。
Excel导出组件支持灵活分页导出,可根据配置调整,减少内存占用。
声明
由于源码逻辑比较复杂,看起来比较晦涩难懂,这里我手写了一个简单的demo,以及通过监控工具、统计数据来逐步分析和优化,感兴趣的可以下载源码跑看看。
环境准备:
Jdk8
Maven
Idea
项目地址:
Demo代码已上传至个人Github:https://github.com/XCXCXCXCX/export-optimization
优化之路
- 传统POI导出
导出组件的诞生就是为了简化业务团队的开发工作,减少代码量,基于aop做出来的第一版,使用了Apache POI做excel数据填充的工作。
使用示例:
@GetMapping
public List<UserDTO> list() {
return userService.list();
}
@GetMapping
@Export
public List<UserDTO> export() {
return userService.list();
}
// 同时需要在UserDTO上声明数据填充映射关系和属性
// 简单示例如下
@ExportDTO
class UserDTO {
@ExportColumn(column="id", width=100)
private long id;
@ExportColumn(column="name", color = Color.RED)
private String name;
}
第一版的逻辑非常简单:
- 查询list数据
- 将list数据填充到一个excel的一个sheet中
- 设置excel下载响应头,写入响应流并结束响应
第一版基本能满足一些小项目的需求。
代码demo如下:
public class ExcelExporterV1 implements ExcelExporter {
private List<String> data;
public ExcelExporterV1(List<String> data) {
this.data = data;
}
@Override
public void export0() {
SXSSFWorkbook workbook = new SXSSFWorkbook();
SXSSFSheet sheet = workbook.createSheet("sheet0");;
for (int i = 0; i < data.size(); i++) {
int index = i / 1000000;
if (index > workbook.getNumberOfSheets() - 1) {
workbook.createSheet("sheet" + index);
}
sheet = workbook.getSheetAt(index);
SXSSFRow row = sheet.createRow(i);
SXSSFCell cell = row.createCell(0);
cell.setCellValue(data.get(i));
}
try {
sheet.flushRows();
} catch (IOException e) {
e.printStackTrace();
}
File file = Static.getFile("v1-" + UUID.randomUUID() + ".xlsx");
try (FileOutputStream fos = new FileOutputStream(file)){
workbook.write(fos);
} catch (IOException e) {
e.<