Easyexcel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel

项目github地址:https://github.com/alibaba/easyexcel

官网地址:https://easyexcel.opensource.alibaba.com/docs/current/

先吐槽一下,官方文档写的太烂了,缺少依赖、逻辑不清晰、复制到idea很多跑起来报错

写Excel

  • 准备工作

创建项目,在pom文件中添加以下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- easyexcel 依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
</dependencies>

创建一个实体类,用于写入excel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
public class Student implements Serializable {

@ExcelProperty(value = {"学生编号"},order = 10)
private Integer id;

@ExcelProperty(value = {"学生姓名"},order = 2)
private String name;

@ExcelProperty(value = {"学生薪水"},order = 1)
private Double salary;

@ExcelProperty(value = {"学生生日"},order = 11)
private Date birthday;
}

最简单的写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//生成数据
public List<Student> getData() {
List<Student> lists = new ArrayList<>();
for(int i = 0; i <= 10; i++) {
Student student = new Student();
student.setId(i + 1);
student.setName("李四" + i);
student.setBirthday(new Date());
student.setSalary(1500.00D);
lists.add(student);
}
return lists;
}

//最简单的写入
@Test
void contextLoads() {
EasyExcel.write("学生信息表.xlsx", Student.class).sheet().doWrite(getData());
}

PS:一下方法都是有关最简单写入的具体方法解释,不感兴趣的可以跳过

write方法

代码解释:

EasyExcel.write源码如下:两个重载的方法,接收文件名(含路径)或文件名和Excel头文件

,new 了一个ExcelWriterBuilder对象,将pathName或head写入excelWriterBuilder并返回

  • pathName:文件名
  • head:写入表格的class对象,如上方Student.class对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class EasyExcelFactory {
/**
* 构建一个Excel写对象
*
* @return
*/
public static ExcelWriterBuilder write() {
return new ExcelWriterBuilder();
}

/**
* 构建一个Excel写对象
*
* @param file 用来写出文件对象
*
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(File file) {
return write(file, null);
}

/**
* 构建 Excel写对象
*
* @param file
* 用来写出的文件对象
* @param head
* 写出的数据类型的class对象
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(File file, Class head) {
ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
excelWriterBuilder.file(file);
if (head != null) {
excelWriterBuilder.head(head);
}
return excelWriterBuilder;
}

/**
* 构建Excel 写对象
*
* @param pathName
* 写出的文件路径名
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(String pathName) {
return write(pathName, null);
}

/**
* 构建excel 写对象
*
* @param pathName
* 写出的文件路径名
* @param head
* 写出数据的数据类型的class对象
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(String pathName, Class head) {
ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
excelWriterBuilder.file(pathName);
if (head != null) {
excelWriterBuilder.head(head);
}
return excelWriterBuilder;
}

/**
* 构建excel写对象
*
* @param outputStream
* 写出的输出流对象
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(OutputStream outputStream) {
return write(outputStream, null);
}

/**
* 构建excel写对象
*
* @param outputStream
* 写出的输出流
* @param head
* 写出数据的数据类型的class对象
* @return Excel writer builder
*/
public static ExcelWriterBuilder write(OutputStream outputStream, Class head) {
ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
excelWriterBuilder.file(outputStream);
if (head != null) {
excelWriterBuilder.head(head);
}
return excelWriterBuilder;
}
}
sheet()方法
1
2
3
4
5
6
7
@Test
void contextLoads() {
EasyExcel
.write("学生信息表.xlsx", Student.class)
.sheet()
.doWrite(getData());
}

sheet()方法代表要在excel那个sheet页写入数据,如果不指定,,默认写在第一个sheet页,默认sheet名:0

源码解释:所有重载的方法可归为一个方法,接收sheetNo(第几个sheet(Integer)),sheetName(sheet页名称(String)),返回ExcelWriterSheetBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
选中第一个sheet页
写操作 sheet 页的值为 0
*/
public ExcelWriterSheetBuilder sheet() {
return sheet(null, null);
}

/*
选中 第一个的 sheet页
sheet 页的名字 为 sheetNo
*/
public ExcelWriterSheetBuilder sheet(Integer sheetNo) {
return sheet(sheetNo, null);
}

/*
选中第一个的 sheet页
sheet 页的名字 为 sheetName
*/
public ExcelWriterSheetBuilder sheet(String sheetName) {
return sheet(null, sheetName);
}

/*
选中第一个 sheet页
sheet 页的名字 为 sheetNo 或者 sheetName
*/
public ExcelWriterSheetBuilder sheet(Integer sheetNo, String sheetName) {
ExcelWriter excelWriter = build();
ExcelWriterSheetBuilder excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
if (sheetNo != null) {
excelWriterSheetBuilder.sheetNo(sheetNo);
}
if (sheetName != null) {
excelWriterSheetBuilder.sheetName(sheetName);
}
return excelWriterSheetBuilder;

doWrite方法

源码如下:

1
2
3
4
5
6
7
8
public void doWrite(List data) {
if (this.excelWriter == null) {
throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method");
} else {
this.excelWriter.write(data, this.build());
this.excelWriter.finish();
}
}

如上可见,doWrite方法只是简单判断了一下当前ExcelWriterSheetBuilder类是否为空,然后调用了write方法,再关闭excelWriter

我们在深入看一下write方法

1
2
3
4
5
6
7
8
public ExcelWriter write(List data, WriteSheet writeSheet) {
return this.write(data, (WriteSheet)writeSheet, (WriteTable)null);
}

public ExcelWriter write(List data, WriteSheet writeSheet, WriteTable writeTable) {
this.excelBuilder.addContent(data, writeSheet, writeTable);
return this;
}

本质上这两个方法属于一个方法,都是接收三个参数,返回ExcelWriter对象

  • List data: 要写入的数据
  • WriteSheet writeSheet: doWrite方法返回ExcelWriterSheetBuilder中的一个属性,存放了sheet信息
  • writeTable: 存放table信息,暂不讨论
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 /** @deprecated */
@Deprecated
public ExcelWriter write(List data, Sheet sheet) {
return this.write(data, (Sheet)sheet, (Table)null);
}

/** @deprecated */
@Deprecated
public ExcelWriter write(List data, Sheet sheet, Table table) {
WriteSheet writeSheet = null;
if (sheet != null) {
writeSheet = new WriteSheet();
writeSheet.setSheetNo(sheet.getSheetNo() - 1);
writeSheet.setSheetName(sheet.getSheetName());
writeSheet.setClazz(sheet.getClazz());
writeSheet.setHead(sheet.getHead());
writeSheet.setTableStyle(sheet.getTableStyle());
writeSheet.setRelativeHeadRowIndex(sheet.getStartRow());
writeSheet.setColumnWidthMap(sheet.getColumnWidthMap());
}
return this.write(data, writeSheet, writeTable);
}

已过时方法,本质如上,根据return语句看,还是调用以上方法,

接着看一下addContent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void addContent(List data, WriteSheet writeSheet) {
this.addContent(data, writeSheet, (WriteTable)null);
}

public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable) {
try {
this.context.currentSheet(writeSheet, WriteTypeEnum.ADD);
this.context.currentTable(writeTable);
if (this.excelWriteAddExecutor == null) {
this.excelWriteAddExecutor = new ExcelWriteAddExecutor(this.context);
}

this.excelWriteAddExecutor.add(data);
} catch (RuntimeException var5) {
this.finishOnException();
throw var5;
} catch (Throwable var6) {
this.finishOnException();
throw new ExcelGenerateException(var6);
}
}

addContent接收了Excel原始数据,通过excelWriteAddExecutor.add写入,具体写入过程可看此方法

表头操作

属性注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
public class Student implements Serializable {

@ExcelProperty(value = {"学生编号"},order = 10)
@NumberFormat("#,###")
private Integer id;

@ExcelProperty(value = {"学生姓名"},order = 2)
private String name;

@ExcelProperty(value = {"学生薪水"},order = 1)
@ExcelIgnore
private Double salary;

@ExcelProperty(value = {"学生生日"},order = 11)
@DateTimeFormat("yyyy-mm-dd")
private Date birthday;
}

如上所见,我们在每个属性上加入了@ExcelProperty注解,表示当前属性是一个excel字段,easyexcel采用了注解的方式定义excel 表头字段,给我们开发带来了极大的便利。

  • ExcelProperty index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头
  • ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段
  • DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat
  • NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat
  • ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与

还有其他注解,地址都在:com.alibaba.excel.annotation