EasyExcel
- EasyExcel重写了POI对07版Excel的解析,可以把内存消耗从100M左右降低到10M以内,并且再大的Excel不会出现内存溢出,03版仍依赖POI的SAX模式。
依赖:
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
常用注解和API
常用类
EasyExcel 入口类,用于构建开始各种操作
ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet
读取时的注解
@ExcelProperty
使用位置:标准作用在成员变量上
可选属性:
属性名 | 含义 | 说明 |
---|---|---|
index | 对应Excel表中的列数 | 默认-1,建议指定时从0开始 |
value | 对应Excel表中的列头 | |
converter | 成员变量转换器 | 自定义转换器需要实Converter接口 |
使用效果:index属性可以指定当前字段对应excel中的哪一列,可以根据列名value去匹配,也可以不写。
如果不使用@ExcelProperty注解,成员变量从上到下的顺序,对应表格中从左到右的顺序;
使用建议:要么全部不写,要么全部用index,要么全部用名字去匹配,尽量不要三个混着用。
// 1. 修改成员变量顺序读取Excel表格
// 2. 修改index属性值读取Excel表格
// 3. 修改value属性值读取Excel表格
@ExcelIgnore
标注在成员变量上,默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
代码演示:
// 4. 忽略id成员变量值读取Excel表格
@DateTimeFormat
标注在成员变量上,日期转换,代码中用String类型的成员变量
去接收excel中日期格式的数据
会调用这个注解。里面的value
参照java.text.SimpleDateFormat
// 5. 按照指定的格式写入Excel内容
@NumberFormat
标注在成员变量上,数字转换,代码中用String类型的成员变量
去接收excel数字格式的数据
会调用这个注解。里面的value
参照java.text.DecimalFormat
@ExcelIgnoreUnannotated
标注在类上。
不标注该注解时,默认类中所有成员变量都会参与读写,无论是否在成员变量上加了@ExcelProperty
的注解。
标注该注解后,类中的成员变量如果没有标注@ExcelProperty
注解将不会参与读写。
读取时通用参数
ReadWorkbook(工作簿ExcelReaderBuilder)
,ReadSheet(工作表ExcelReaderSheetBuilder)
都会有的参数👀👀,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。readListener
监听器,在读取数据的过程中会不断的调用监听器。headRowNumber
指定需要读表格的 列头行数。默认有一行头,也就是认为第二行开始起为数据。head
与clazz
二选一。读取文件头对应的列表,会根据列表匹配数据。建议使用clas,就是文件中每一行数据对应的代码中的实体类型。clazz
与head
二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。autoTrim
字符串、表头等数据自动trimpassword
读的时候是否需要使用密码
ReadWorkbook(工作簿对象)参数
excelType
当前excel的类型,读取时会自动判断,无需设置。inputStream
与file
二选一。建议使用file。file
与inputStream
二选一。读取文件的文件。autoCloseStream
自动关闭流。readCache
默认小于5M用 内存,超过5M会使用EhCache
,不建议使用这个参数。useDefaultListener
@since 2.1.4
默认会加入ModelBuildEventListener
来帮忙转换成传入class
的对象,设置成false
后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>
对象,如果还想继续接听到class
对象,请调用readListener
方法,加入自定义的beforeListener
、ModelBuildEventListener
、 自定义的afterListener
即可。
ReadSheet(工作表对象)参数
sheetNo
需要读取Sheet的编号,建议使用这个来指定读取哪个SheetsheetName
根据名字去匹配Sheet,excel 2003不支持根据名字去匹配
写入时的注解
@ExcelProperty
使用位置:标准作用在成员变量上
可选属性:
属性名 | 含义 | 说明 |
---|---|---|
index | 对应Excel表中的列数 | 默认-1,指定时建议从0开始 |
value | 对应Excel表中的列头 | |
converter | 成员变量转换器 | 自定义转换器需要实Converter接口 |
使用效果:index
指定写到第几列,如果不指定则根据成员变量位置排序;
value
指定写入的列头,如果不指定则使用成员变量的名字作为列头;
如果要设置复杂的头,可以为value指定多个值。
其他注解:
基本和读取时一致
- @ContentRowHeight() 标注在类上或属性上,指定内容行高
- @HeadRowHeight() 标注在类上或属性上,指定列头行高
- @ColumnWidth() 标注在类上或属性上,指定列宽
- @ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段
- @DateTimeFormat
日期转换,将
Date写到excel会调用这个注解。里面的
value参照
java.text.SimpleDateFormat - @NumberFormat
数字转换,用
Number写excel会调用这个注解。里面的
value参照
java.text.DecimalFormat` - @ExcelIgnoreUnannotated
默认不加
ExcelProperty` 的注解的都会参与读写,加了不会参与
写入时通用参数
ReadWorkbook(工作簿ExcelReaderBuilder)
,ReadSheet(工作表ExcelReaderSheetBuilder)
都会有的参数👀👀,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。writeHandler
写的处理器。可以实现WorkbookWriteHandler
,SheetWriteHandler
,RowWriteHandler
,CellWriteHandler
,在写入excel的不同阶段会调用,对使用者透明不可见。relativeHeadRowIndex
距离多少行后开始。也就是开头空几行needHead
是否导出头head
与clazz
二选一。写入文件的头列表,建议使用class。clazz
与head
二选一。写入文件的头对应的class,也可以使用注解。autoTrim
字符串、表头等数据自动trim
WriteWorkbook(工作簿对象)参数
excelType
当前excel的类型,默认为xlsx
outputStream
与file
二选一。写入文件的流file
与outputStream
二选一。写入的文件templateInputStream
模板的文件流templateFile
模板文件autoCloseStream
自动关闭流。password
写的时候是否需要使用密码useDefaultStyle
写的时候是否是使用默认头
WriteSheet(工作表对象)参数
sheetNo
需要写入的编号。默认0sheetName
需要些的Sheet名称,默认同sheetNo
读取Excel
文件
实体-注意:实体的属性要与表格中的数据位置对应一直
/**
* Description:
* Created by 大师兄 on 2021/11/20 16:39
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
/**
* 学生姓名
*/
private String name;
/**
* 学生性别
*/
private String gender;
/**
* 学生出生日期
*/
private String birthday;
/**
* id
*/
private String id;
}
调用EasyExcel
的API
读取的Excel
文件的测试类StudentReadDemo
/**
* Description:
* Created by 大师兄 on 2021/11/20 17:01
*/
public class StudentReadDemo {
public static void main(String[] args) throws FileNotFoundException {
// 读取文件,读取完之后会自动关闭
/*
pathName 文件路径;"d:\\test.xls"
head 每行数据对应的实体;Student.class
readListener 读监听器(继承AnalysisEventListener),每读一样就会调用一次该监听器的invoke方法
sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字,不传默认为0
*/
// 封装工作簿对象
ExcelReaderBuilder workBook = EasyExcel.read
("d:\\test.xlsx", Student.class, new StudentReadListener());
//workBook.headRowNumber(3); 从索引为3的位置开始读
// 封装工作表
ExcelReaderSheetBuilder sheet1 = workBook.sheet();//默认获取第一个工作表
// 读取
sheet1.doRead();
}
}
test.xls:
StudentReadListener:
/**
* Description:
* Created by 大师兄 on 2021/11/20 16:53
*/
public class StudentReadListener extends AnalysisEventListener<Student> {
// 每读一行,会调用该invoke方法一次,在invoke可以操作读取到的数据
public void invoke(Student data, AnalysisContext context) {
System.out.println("data = " + data);
}
// 全部读完之后,会调用该方法
public void doAfterAllAnalysed(AnalysisContext context) {
// TODO......
}
}
text.xlsx数据:
姓名 性别 生日 ID
TOM boy 2021/11/20 1
BOB boy 2021/11/20 2
SYC boy 2021/11/20 3
Sovzn boy 2021/11/20 4
JJK boy 2021/11/20 5
ZS girl 2021/11/20 6
LS girl 2021/11/20 7
WMZ girl 2021/11/20 8
ZH girl 2021/11/20 9
JJK girl 2021/11/20 10
执行结果:
data = Student(name=TOM, gender=boy, birthday=2021-11-20 00:00:00, id=1)
data = Student(name=BOB, gender=boy, birthday=2021-11-20 00:00:00, id=2)
data = Student(name=SYC, gender=boy, birthday=2021-11-20 00:00:00, id=3)
data = Student(name=Sovzn, gender=boy, birthday=2021-11-20 00:00:00, id=4)
data = Student(name=JJK, gender=boy, birthday=2021-11-20 00:00:00, id=5)
data = Student(name=ZS, gender=girl, birthday=2021-11-20 00:00:00, id=6)
data = Student(name=LS, gender=girl, birthday=2021-11-20 00:00:00, id=7)
data = Student(name=WMZ, gender=girl, birthday=2021-11-20 00:00:00, id=8)
data = Student(name=ZH, gender=girl, birthday=2021-11-20 00:00:00, id=9)
data = Student(name=JJK, gender=girl, birthday=2021-11-20 00:00:00, id=10)
Process finished with exit code 0
写操作:
到处数据的实体:
/**
* Description:
* Created by 大师兄 on 2021/11/20 17:22
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ColumnWidth(20) //栏目宽度
//@HeadRowHeight()
public class Student_write {
/**
* id
*/
//@ExcelProperty(value = "编号",index = 3)
@ExcelIgnore
private String id;
/**
* 学生姓名
*/
@ExcelProperty(value = {"信息表","学生姓名"} index = 0)
//@ColumnWidth(30)
private String name;
/**
* 学生性别
*/
@ExcelProperty(value = "学生性别", index = 2)
private String gender;
/**
* 学生出生日期
*/
@ExcelProperty(value = "学生出生日期", index = 1)
@DateTimeFormat("yyyy-MM-dd")
//@ColumnWidth(20)
private Date birthday;
}
/**
* Description:
* Created by 大师兄 on 2021/11/20 17:26
*/
public class StudentWriteDemo {
public static void main(String[] args) {
List<Student_write> Student_writes = initData();
/*
String pathName 写入文件的路径
Class head 写入文件的对象类型
默认写入到07的xlsx中,如果想要写入xls,可以指定类型(待验证)
*/
ExcelWriterBuilder workBook = EasyExcel.write("d:\\测试表.xlsx", Student_write.class);
// sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字
workBook.sheet().doWrite(Student_writes);
}
private static List<Student_write> initData() {
ArrayList<Student_write> Student_writes = new ArrayList<Student_write>();
Student_write data = new Student_write();
for (int i = 0; i < 10; i++) {
data.setName("Sovzn" + i);
data.setBirthday(new Date());
data.setGender("男");
Student_writes.add(data);
}
return Student_writes;
}
}
填充
填充一组数据
Excel表格中用{} 来表示包裹要填充的变量,如果单元格文本中本来就有{
、}
左右大括号,需要在括号前面使用斜杠转义\{
、\}
。
代码中用来填充数据的实体对象的成员变量名或用来填充map集合的key需要和Excel中被{}包裹的变量名称一致。
fill_data_template1.xlsx:
/**
* 使用实体类封装填充数据
*
* 实体中成员变量名称需要和Excel表各种{}包裹的变量名匹配
*/
@Data
public class FillData {
private String name;
private int age;
}
/**
* Description:
* Created by 大师兄 on 2021/11/20 17:26
*/
public class StudentFillDemo {
public static void main(String[] args) {
// 准备模板
String templateFile = "d:\\JavaFileIOTest\\fill_data_template1.xlsx";
//String templateFile = "fill_data_template1.xlsx";
// 写入文件:当前项目下
String targetFileName = "d:\\JavaFileIOTest\\单组数据填充.xlsx";
// 准备对象数据填充
FillData fillData = new FillData();
fillData.setName("shiyaochang");
fillData.setAge(10);
// 创建一个工作簿对象
ExcelWriterBuilder workBookWriter = EasyExcel.write(targetFileName).withTemplate(templateFile);
// 创建一个工作表
ExcelWriterSheetBuilder sheet = workBookWriter.sheet();
// 获取工作表并填充
sheet.doFill(fillData);
// 使用Map数据填充
//HashMap<String, String> mapFillData = new HashMap<>();
//mapFillData.put("name", "Map");
//mapFillData.put("age", "11");
// 获取第一个工作表填充并自动关闭流
//workBookWriter.sheet().doFill(mapFillData);
}
}
填充多组数据
Excel表格中用{.}
来表示包裹要填充的变量,如果单元格文本中本来就有{
.}
左右大括号,需要在括号前面使用斜杠转义\{
.\}
。
代码中被填充数据的实体对象的成员变量名或被填充map集合的key需要和Excel中被{}包裹的变量名称一致。
fill_data_template2.xlsx:
/**
* Description:
* Created by 大师兄 on 2021/11/20 17:26
*/
public class StudentMutiFillDemo {
public static void main(String[] args) {
// 准备模板
String templateFile = "d:\\JavaFileIOTest\\fill_data_template2.xlsx";
//String templateFile = "fill_data_template1.xlsx";
// 写入文件:当前项目下
String targetFileName = "d:\\JavaFileIOTest\\多组数据填充.xlsx";
// 准备对象数据填充
FillData fillData = new FillData();
fillData.setName("shiyaochang");
fillData.setAge(10);
// 创建一个工作簿对象
ExcelWriterBuilder workBookWriter = EasyExcel.write(targetFileName).withTemplate(templateFile);
// 创建一个工作表
ExcelWriterSheetBuilder sheet = workBookWriter.sheet();
List<FillData> fillDatas = initData();
// 获取工作表并填充
sheet.doFill(fillDatas);
}
private static List<FillData> initData() {
ArrayList<FillData> FillDatas = new ArrayList<FillData>();
for (int i = 0; i < 10; i++) {
FillData data = new FillData();
data.setName("Sovzn" + i);
data.setAge(10);
FillDatas.add(data);
}
return FillDatas;
}
}
效果:
组合填充
既有单组数据又有多组数据的填充,注:在EasyExcel中执行doXXX方法后会自动关闭流,因此,组合填充中不能再使用doXXX,方式填充完一组数据后关闭流,使后面的数据无法填充。
fill_data_template3.xlsx:
/**
* Description:
* Created by 大师兄 on 2021/12/06 21:19
*/
public class CombineFillDemo {
public static void main(String[] args) {
// 准备模板
String templateFile = "d:\\JavaFileIOTest\\fill_data_template3.xlsx";
//String templateFile = "fill_data_template1.xlsx";
// 写入文件:当前项目下
String targetFileName = "d:\\JavaFileIOTest\\组合数据填充.xlsx";
// 创建一个工作簿对象(组合填充时,用build创建工作簿)
ExcelWriter workbook = EasyExcel.write(targetFileName).withTemplate(templateFile).build();
// 创建一个工作表
WriteSheet sheet = EasyExcel.writerSheet().build();
//数据(单组):
Map<String, String> DateAndTotal = new HashMap<String, String>() {{
put("date", "2021-1-1");
put("total", "目标001");
}};
//数据(多组):
List<FillData> fillDatas = initData();
// 组合填充时,因为多组填充的数据量不确定,需要在多组填充完之后另起一行,否则在填充时可能会覆盖下方的单组数据(date和total)
FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
//填充:不适用doXXX,直接使用fill
//1.多组数据填充:
workbook.fill(fillDatas,fillConfig, sheet);
//2.单组数据填充:
workbook.fill(DateAndTotal, sheet);
//切记:手动关流
workbook.finish();
}
private static List<FillData> initData() {
ArrayList<FillData> FillDatas = new ArrayList<FillData>();
for (int i = 0; i < 10; i++) {
FillData data = new FillData();
data.setName("Sovzn" + i);
data.setAge(10);
FillDatas.add(data);
}
return FillDatas;
}
}
水平填充
填充时需要通过FillConfig
对象设置水平填充。
fill_data_template4.xlsx:
/**
* Description:
* Created by 大师兄 on 2021/12/06 22:03
*/
public class HORIZONTAL_Fill {
public static void main(String[] args) {
// 准备模板
String templateFile = "d:\\JavaFileIOTest\\fill_data_template4.xlsx";
//String templateFile = "fill_data_template1.xlsx";
// 写入文件:当前项目下
String targetFileName = "d:\\JavaFileIOTest\\水平数据填充.xlsx";
// 创建一个工作簿对象(组合填充时,用build创建工作簿)
ExcelWriter workbook = EasyExcel.write(targetFileName).withTemplate(templateFile).build();
// 创建一个工作表
WriteSheet sheet = EasyExcel.writerSheet().build();
//数据:
List<FillData> fillDatas = initData();
//-------设置水平填充---------
FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
//填充
workbook.fill(fillDatas,fillConfig, sheet);
//切记:手动关流
workbook.finish();
}
private static List<FillData> initData() {
ArrayList<FillData> FillDatas = new ArrayList<FillData>();
for (int i = 0; i < 10; i++) {
FillData data = new FillData();
data.setName("Sovzn" + i);
data.setAge(10);
FillDatas.add(data);
}
return FillDatas;
}
}
- Post link: http://sovzn.github.io/2021/11/20/EasyExcel/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues