EasyExcel

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

这里只是EasyExcel的简单导入和导出操作,更多复杂的场景可以参考Easy Excel

1.引入依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>

2.文件导入

2.1 基本导入

文件内容如下:

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
//示例:从本地file文件导入
@Test
public void test(){
//读取文件
File file = new File("src/main/resources/template/test.xlsx");
//读取工作表 sheet(0)表示第一张表,可以省略,默认即为0
List<Object> sheet = EasyExcel.read(file).sheet(0).doReadSync();
//遍历首行除外的每一行
for (Object row : sheet) {
log.info(row.toString());
}
}

//示例:InputStream输入流中导入
@Test
public void test2(){
//读取文件
//xxx.class指当前类名
//文件路径是项目resources下的路径
InputStream inputStream =
EasyExcelTest.class.getClassLoader().getResourceAsStream("template/test.xlsx");
//读取工作表 sheet(0)表示第一张表,可以省略,默认即为0
List<Object> sheet = EasyExcel.read(inputStream).sheet(0).doReadSync();
//遍历首行除外的每一行
for (Object row : sheet) {
log.info(row.toString());
}
}

执行结果:

可以看到读取结果的类型是一个**List<Map<Integer,String>>,所以也可以直接用这个代替List<Object>。但需要注意的是,在Excel中,需要将格式设定为文本,否则在遇到比如日期格式**的单元格时无法接收会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//示例:从本地file文件导入
@Test
public void test3(){
//读取文件
File file = new File("src/main/resources/template/test.xlsx");
//读取工作表 sheet(0)表示第一张表,可以省略,默认即为0
//用List<Map<Integer,String>>接收返回对象
List<Map<Integer,String>> sheet = EasyExcel.read(file).sheet(0).doReadSync();
//遍历首行除外的每一行
for (Map row : sheet) {
log.info("昵称:{}\t性别:{}\t邮箱:{}\t生日:{}\t积分:{}",
row.get(0),row.get(1),row.get(2),row.get(3),row.get(4));
}
}

2.2 模型映射导入

创建对应的模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfoModel {
//昵称
private String name;
//性别
private String gender;
//邮箱
private String email;
//生日 //也可以使用String
private Date birthday;
//积分
private Integer score;
}

使用方法

和上面的方法区别在于多了一个.head(UserInfoModel.clss),并且返回值用List<UserInfoModel>接收

而在获取的单元格的时候就可以直接row.getXxx();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test3(){
//读取文件
File file = new File("src/main/resources/template/test.xlsx");
//读取工作表 sheet(0)表示第一张表,可以省略,默认即为0
List<UserInfoModel> sheet =
EasyExcel.read(file).head(UserInfoModel.class).sheet(0).doReadSync();
//遍历首行除外的每一行
for (UserInfoModel row : sheet) {
log.info("昵称:{}\t性别:{}\t邮箱:{}\t生日:{}\t积分:{}",
row.getName(),
row.getGender(),
row.getEmail(),
row.getBirthday(),
row.getScore());
}
}

常用注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfoModel {
//昵称
@ExcelProperty(value = "昵称",index = 0)
private String name;
//性别
@ExcelProperty("性别")//一般建议只使用value
private String gender;
//邮箱
@ExcelProperty("邮箱")
private String email;
//生日
//注意在Excel中的生日应该是文本格式,否则导入会报错
@ExcelProperty("生日")
@DateTimeFormat(value = "yyyy-MM-dd")//这里的格式应该和文件中的格式一样
private Date birthday;
//积分
@ExcelProperty("积分")
private Integer score;
}

3.文件导出

3.1 基本导出

最基本的导出可以用List<List<Object>>List<Map<Integer,Object>>List<Object>

即每一行可以是List、Map 或 对象

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
private final String PATH = "src/main/resources/template/test2.xlsx";
//List<List<Object>> List导出
@Test
public void test4(){
List<Object> list1 = Arrays.asList(1,2,3);
List<Object> list2 = Arrays.asList("到拉萨机","收集大量","看到");

List<List<Object>> list = Arrays.asList(list1,list2);
EasyExcel.write(PATH)
.sheet("导出1")
.doWrite(list);
}

//List<Map<Integer,Object>> Map导出
@Test
public void test5(){
Map<Integer,Object> map1 = new HashMap<>();
map1.put(0,1);
map1.put(1,2);
map1.put(2,3);
Map<Integer,Object> map2 = new HashMap<>();
map2.put(0,"郭德纲");
map2.put(1,"郭德纲");
map2.put(2,"郭德纲");

List<Map<Integer,Object>> list = Arrays.asList(map1,map2);
EasyExcel.write(PATH)
.sheet("导出2")
.doWrite(list);
}

//List<Object> 对象导出
@Test
public void test6(){
//同一次导出只能识别一种对象
UserInfoModel user1 = new UserInfoModel();
user1.setName("郭德纲");
user1.setGender("男");
UserInfoModel user2 = new UserInfoModel();
user2.setName("玉泉山");
user2.setGender("男");

List<Object> list = Arrays.asList(user1,user2);
EasyExcel.write(PATH)
.sheet("导出3")
.doWrite(list);
}

3.2 模型映射导出

模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
//注解的使用与导入时相类似
//使用了注解,就会自动产生表头
@ExcelProperty("名字")
private String name;
@ExcelProperty("年龄")
private Integer age;
@ExcelProperty("生日")
@DateTimeFormat("yyyy年MM月dd日")
private Date birthDay;
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j
public class ExcelTest {
private final String PATH ="src/main/resources/test.xlsx";
@Test
public void test1(){
User user1 = User.builder()
.name("刘国泰")
.age(12)
.birthDay(new Date())
.money(BigDecimal.valueOf(12.34))
.build();

List<Object> list = Arrays.asList(user1);
EasyExcel.write(PATH)
.sheet("导出4")
.head(User.class)//与对象导出的区别在这
.doWrite(list);
}
}

结果

4.模板填充

4.1 填充对象

首先需要准备一个模板,然后在需要填充的地方写上占位符,比如**{name}**,然后执行时就会根据name,将对应的值填充进去

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
private final String PATH ="src/main/resources/test.xlsx";
//使用Map填充
@Test
public void test2(){
//读取模板
File template = new File("src/main/resources/template.xlsx");
//封装数据
Map<String,Object> map = new HashMap<>();
map.put("name","王建国");
map.put("age",18);
map.put("birthDay",new Date());
map.put("money",BigDecimal.valueOf(1888.88));
//填充
EasyExcel.write(PATH) //输出路径
.withTemplate(template) //输入模板
.sheet()
.doFill(map); //执行填充

}

//使用映射模型填充
@Test
public void test3(){
//读取模板
File template = new File("src/main/resources/template.xlsx");
//封装数据
User user = User.builder()
.name("王建国")
.age(55)
.birthDay(new Date())
.money(BigDecimal.valueOf(2888.88))
.build();
//填充
EasyExcel.write(PATH) //输出路径
.withTemplate(template) //输入模板
.sheet()
.doFill(user); //执行填充

}

结果

4.2 填充列表

批量填充的模板中,参数与填充单个对象的写法不同,需要在填充对应的键名前加一个点。比如**{.name}**

代码上和填充对象几乎没有区别,唯一的区别在于传入的填充参数是一个List

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
@Test
public void test4(){
//读取模板
File template = new File("src/main/resources/template.xlsx");
//封装数据
User user1 = User.builder()
.name("王建国")
.age(55)
.birthDay(new Date())
.money(BigDecimal.valueOf(2888.88))
.build();
User user2 = User.builder()
.name("川建国")
.age(65)
.birthDay(new Date())
.money(BigDecimal.valueOf(888888.88))
.build();
List<User> list = Arrays.asList(user1,user2);

//填充
EasyExcel.write(PATH) //输出路径
.withTemplate(template) //输入模板
.sheet()
.doFill(list); //执行填充

}

结果

4.3 组合填充

在某些场景下,可能既需要填充单个的值,也需要一次性填充多条数据,这种情况下就需要使用组合填充

在这种场景下,模板可以像下面这样写

代码

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
//组合填充

//输出路径
private final String PATH ="src/main/resources/test.xlsx";
@Test
public void test5(){
//读取模板
File template = new File("src/main/resources/template2.xlsx");
//生成一个writer来进行多次的写入
try( ExcelWriter writer = EasyExcel.write(PATH).withTemplate(template).build() ){
//新建工作表
WriteSheet sheet = EasyExcel.writerSheet().build();

//填充单个值
Map map = new HashMap<>();
map.put("newCount",123);
writer.fill(map,sheet);

//批量填充
User user1 = User.builder()
.name("王建国")
.age(55)
.birthDay(new Date())
.money(BigDecimal.valueOf(2888.88))
.build();
User user2 = User.builder()
.name("川建国")
.age(65)
.birthDay(new Date())
.money(BigDecimal.valueOf(888881.88))
.build();
List list = Arrays.asList(user1,user2);
writer.fill(list,sheet);
}

}

结果