JAVA解析Excel工具EasyExcel

官方讲解:Java解析、生成Excel比较有名的框架有Apache poi、jxl。
但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API
可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版
Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI
sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,
并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,
在上层做了模型转换的封装,让使用者更加简单方便
Ps: 64M内存20秒读取75M(46W行25列)的Excel(3.0.2+版本)

版本支持

2+ 版本支持 Java7和Java6
3+ 版本至少 Java8

maven依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- EasyExcel  -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>

实体类

1.实体类
注解:

1.@Data -> lombok插件 注解 使用之后自动生成属性的get/set方法
2.@AllArgsConstructor -> lombok插件 注解 使用之后生成全部的构造方法不含无参构造
3.@NoArgsConstructor -> lombok插件 注解 使用之后生成无参构造
4.@ExcelProperty(value = “Excel标题名称”) -> easyExcel 注解
5.@ColumnWidth(35) -> easyExcel 注解 列宽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @Author jiushiboy
* @Date 2021/11/22 15:36
* @Version 1.0
*/

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JiuShiBoy{
@ExcelProperty(value = "年龄")
@ColumnWidth(35)
private Integer age;

@ExcelProperty(value = "名称")
@ColumnWidth(35)
private String name;

@ExcelProperty(value = "邮箱")
@ColumnWidth(35)
private String email;
}

封装EasyExcel工具类(百度找的)

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
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
* 高性能处理Excel工具类
*
* @author jiushiboy
*
*/
public class EasyExcelUtil {
/**
* 使用 模型 来读取Excel
*
* @param inputStream Excel的输入流
* @param clazz 模型的类
* @return 返回 模型 的列表
*/
public static <T> List<T> readExcel(InputStream inputStream, Class<T> clazz) {
ModelExcelListener<T> listener = new ModelExcelListener<T>();
EasyExcel.read(inputStream, clazz, listener).sheet().doRead();
return listener.getDatas();
}

/**
* 使用 模型 来导出到WEB
*
* @param response web的响应
* @param data 要写入的以 模型 为单位的数据
* @param fileName 配置Excel的表名
* @param sheetName 配置Excel的页签名
* @param clazz 模型的类
* @throws IOException
*/
public static <T> void writeExcel(HttpServletResponse response, List<T> data, Class<T> clazz, String fileName, String sheetName) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
}

/**
* 使用 模型 来写入Excel
* <br/>注意,如果是web输出流,需要设置头
*
* @param outputStream Excel的输出流
* @param data 要写入的以 模型 为单位的数据
* @param sheetName 配置Excel的表名字
* @param clazz 模型的类
*/
public static <T> void writeExcel(OutputStream outputStream, List<T> data, Class<T> clazz, String sheetName) {
EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(data);
}

/**
* 模型 解析监听器
*/
private static class ModelExcelListener<T> extends AnalysisEventListener<T> {
private List<T> datas = new ArrayList<>();

@Override
public void invoke(T object, AnalysisContext context) {
datas.add(object);
}

@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}

public List<T> getDatas() {
return datas;
}
}

}

导入控制层Controller

注解:

@Api(value = “swagger插件上显示的注释”)
@RestController -> SpringBoot注解 = @ResponseBody + @Controller
@RequestMapping(“/easyExcelImportController”) -> Spring注解 请求接口路径需要加上注解中定义的值 假设 localhost:8080/easyExcelImportController
@Slf4j 日志注解 当类上定义此注解则会打印日志,使用 log.info(“这里是日志:{}”,”这个入参是object 一般会将入参通过FastJson转json传入JsonObject.toJsonString(object)”);
@ApiParam(“file) swagger注解
@RequestPart(“file”) Spring注解 主要接收表单数据
HttpServletRequest 代表客户端请求

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
@Api(value = "swagger上显示的注释")
@RestController
@RequestMapping("/easyExcelImportController")
@Slf4j
public class TargetManageController {

@ApiOperation(value = "excel导入操作", notes = "excel导入操作")
@PostMapping(value = "/importing", consumes = "multipart/*", headers = "content-type=multipart/form-data")
public void importing(@ApiParam(name = "file", value = "导入的文件", required = true) @RequestPart("file") MultipartFile file, HttpServletRequest request) {
try {
importService.importing(file);
} catch (Exception | IOException e) {
log.error("导入数据出现异常:{}", e.getMessage());
}
}

@ApiOperation(value = "excel导出", notes = "excel导出")
@RequestMapping(value = "/leadingOut")
public void leadingOut(HttpServletResponse response) {
try {
List<JiuShiBoy> result = targetMangeService.leadingOut();
EasyExcelUtil.writeExcel(response, result, JiuShiBoy.class, "jiushiboy", "sheet1");
} catch (IOException e) {
log.error("导出出现异常:{}", e);
}
}
}

导入Service 业务层

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
/**
* @Author wenchang
* @Date 2021/11/16 10:19
* @Version 1.0
*/
@Service
@RefreshScope
@Slf4j
public class ImportService {
/***
* @description: 导入演示
* @param: file
* @date: 2022/01/28 17:24
*/
public void importing(MultipartFile file) throws IOException {
// 使用工具类直接转成类型数据 参数一:输入流对象 参数二:转成什么对象类型
List<JiuShiBoy> targetExcelManages = EasyExcelUtil.readExcel(file.getInputStream(), JiuShiBoy.class);
// 然后就随便你操作咯
}
/***
* @description: 导出演示
* @date: 2022/01/28 17:24
*/
public List<JiuShiBoy> leadingOut() {
// 这里我就不弄数据了 返回数据之后看Controller
return new ArrayList<JiuShiBoy>();
}
}