一、初识EasyExcel
1. Apache POI
有过报表导入导出经验的同学,应该有听说或使用过POI。
Apache POI是Apache软件基金会的开源函式库,提供跨平台的Java API实现Microsoft Office格式档案读写。但是存在如下一些问题:
1.1 学习使用成本较高
对POI有过深入了解才知道原来POI还有SAX模式(Dom解析模式)。但SAX模式相对比较复杂,excel有03和07两种版本,两个版本数据存储方式截然不同,sax解析方式也各不一样。
想要了解清楚这两种解析方式,才去写代码测试,估计两天时间是需要的。再加上即使解析完,要转换到自己业务模型还要很多繁琐的代码。总体下来感觉至少需要三天,由于代码复杂,后续维护成本巨大。
POI的SAX模式的API可以一定程度的解决一些内存溢出的问题,但是POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大,一个3M的Excel用POI的SAX解析,依然需要100M左右内存。
1.2 POI的内存消耗比较大
大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。
总体上来说,简单写法重度依赖内存,复杂写法学习成本高。
1.3 特点
-
功能强大
-
代码书写冗余繁杂
-
读写大文件耗费内存较大,容易OOM
2. EasyExcel
2.1 重写了POI对07版Excel的解析
-
EasyExcel重写了POI对07版Excel的解析,可以把内存消耗从100M左右降低到10M以内,并且再大的Excel不会出现内存溢出,03版仍依赖POI的SAX模式。
-
下图为64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)
-
在上层做了模型转换的封装,让使用者更加简单方便
2.2 特点
-
在数据模型层面进行了封装,使用简单
-
重写了07版本的Excel的解析代码,降低内存消耗,能有效避免OOM
-
只能操作Excel
-
不能读取图片
二、快速入门--QuickStart
1. 导入依赖坐标
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
<!-- lombok 优雅编程 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2. 最简单的读
2.1 需求
/**
* 需求:单实体导入
* 导入Excel学员信息到系统。
* 包含如下列:姓名、性别、出生日期
*/
2.2 编写导出数据的实体
// 基于lombok
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
/**
* 学生姓名
*/
private String name;
/**
* 学生性别
*/
private String gender;
/**
* 学生出生日期
*/
private Date birthday;
/**
* id
*/
private String id;
}
2.3 读取Excel文件
@Test
public void test01(){
/**
* @param pathName
* 要赌的文件的路径
* @param head
* 文件中每一行数据要存储到的实体的类型的class
* @param pathName
* 读监听器,梅读一行内容,救护调用一次该对象的invoke,在invoke可以操作使用读取到的数据
*
*/
//获取一个工作簿对象
ExcelReaderBuilder readWorkBook = EasyExcel.read("杭州黑马在线202003班学员信息表.xlsx", Student.class, new StudentListener());
//获得一个工作表对象
ExcelReaderSheetBuilder sheet = readWorkBook.sheet();
//读取工作表中内容
sheet.doRead();
}
读取Excel的监听器,用于处理读取产生的数据
//读取文档的监听器类
public class StudentListener extends AnalysisEventListener<Student> {
/**
* 梅毒一行数据,就会调用一次invoke,在invoke可以操作使用读取到的数据
* @param student 每次读取到的数据封装的对象
* @param analysisContext
*/
public void invoke(Student student, AnalysisContext analysisContext) {
System.out.println("student = " + student);
}
/**
* 读取完整个文档之后调用的方法
* @param analysisContext
*/
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
//读监听器,梅读一行内容,救护调用一次该对象的invoke,在invoke可以操作使用读取到的数据
}
3. 最简单的写
3.1 需求、准备工作
/**
* 需求:单实体导出
* 导出多个学生对象到Excel表格
* 包含如下列:姓名、性别、出生日期
*/
3.2 编写导出数据的实体
@Data
@AllArgsConstructor
@NoArgsConstructor
//@ContentRowHeight //内容的行高
//@HeadRowHeight //表头的行高
public class Student {
/**
* 学生姓名
*/
// @ExcelProperty("学生姓名")
// @ColumnWidth(20)
private String name;
/**
* 学生出生日期
*/
// @ExcelProperty("学生出生日期")
// @ColumnWidth(20)
// @DateTimeFormat("yyy-MM-dd")
private Date birthday;
/**
* 学生性别
*/
// @ExcelProperty("学生性别")
private String gender;
/**
* id
*/
// @ExcelProperty("id")
// @ExcelIgnore //创建表格时忽略该属性
private String id;
}
3.3 准备数据并写入到文件
@Test
public void test02(){
/**
* 构建一个写的工作簿对象
*/
//工作簿对象
ExcelWriterBuilder write = EasyExcel.write("杭州黑马在线202003班学员信息表-write.xlsx", Student.class);
//工作表对象
ExcelWriterSheetBuilder sheet = write.sheet();