【引言】
之前项目中做导入导出功能,都是集成Apache开源框架 poi,也许我们还遇到过线上数据量过大,导致OOM。 所以,本篇博客使用的是阿里的框架EasyExcel。
【概述】
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
【使用】
1. 添加EasyExcel依赖,代码如下:
<!--alibaba easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>
2. 定义导出Excel数据集的数据模型,如ArticleWriteModel,代码如下:
/**
* @Description : 导出文章Excel数据集
* @Author : huzhiting
* @Date: 2020-03-24 18:12
*/
@Data
public class ArticleWriteModel extends BaseRowModel {
@ExcelProperty(value = "编号", index = 0)
private Long id;
@ExcelProperty(value = "编码", index = 1)
private String code;
@ExcelProperty(value = "标题", index = 2)
private String title;
@ExcelProperty(value = "关键字", index = 3)
private String keywords;
@ExcelProperty(value = "作者", index = 4)
private String author;
@ExcelProperty(value = "发布时间", index = 5)
private String publishTime;
}
ExayExcel 提供注解的方式, 来方便的定义 Excel 需要的数据模型:
i. 定义的写入模型必须要继承自 BaseRowModel.java;
ii. 通过 @ExcelProperty 注解来指定每个字段的列名称,以及下标位置;
3. 定义导出类,添加导出Excel方法,代码如下:
public class ExportFile {
private static final int SIZE_OF_PER_SHEET = 100;
/**
* 写入Excel,每页sheet默认100条数据,大于则创建新的sheet页
* @param pathName
* @param dataInfo
* @param T
* @throws Exception
*/
public void writeExcel(String pathName, List<? extends BaseRowModel> dataInfo,Class<?> T) throws Exception {
ExcelWriter writer = null;
// 文件输出位置
OutputStream out = null;
try {
out = new FileOutputStream(pathName);
writer = EasyExcelFactory.getWriter(out);
int currentSheet = 1;
int leftRecords = dataInfo.size();
do{
Sheet sheet = createNewSheet(currentSheet,T);
int fromIndex = (currentSheet - 1) * SIZE_OF_PER_SHEET;
int toIndex = fromIndex + (leftRecords > SIZE_OF_PER_SHEET ? SIZE_OF_PER_SHEET : leftRecords);
// 写数据到 Writer 上下文中
// 入参1: 创建要写入的模型数据
// 入参2: 要写入的目标 sheet
writer.write(dataInfo.subList(fromIndex,toIndex), sheet);
currentSheet ++ ;
leftRecords-=SIZE_OF_PER_SHEET;
}while (leftRecords > 0);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
// 将上下文中的最终 outputStream 写入到指定文件中
writer.finish();
// 关闭流
out.close();
}
}
/**
* 创建新的sheet页
* @param currentSheet
* @param T
* @return
*/
private Sheet createNewSheet(int currentSheet, Class T) {
// 创建新的Sheet
Sheet sheet = new Sheet(currentSheet, 0, T);
// sheet 名称
sheet.setSheetName("Sheet" + currentSheet);
return sheet;
}
}
4. 新建测试类,测试导出所有文章到本地指定目录下,代码如下:
@Test
public void testExportArticle() throws Exception {
List<Article> articleList = articleService.list();
List<ArticleWriteModel> records = new ArrayList<>();
if(!CollectionUtils.isEmpty(articleList)){
articleList.forEach(x -> {
ArticleWriteModel articleWriteModel = new ArticleWriteModel();
BeanUtils.copyProperties(x,articleWriteModel);
articleWriteModel.setPublishTime(LocalDateUtil.formatDate(x.getPublishTime(),"yyyy-MM-dd HH:mm:ss"));
records.add(articleWriteModel);
});
}
String now = LocalDateUtil.formatDate(LocalDateTime.now(),"yyyyMMddHHmmssSSS");
String pathName = "F:\\excel\\" + "article" + now + ".xlsx";
exportFile.writeExcel(pathName,records,ArticleWriteModel.class);
}
测试结果如下:
完整代码地址: