1. EasyExcel介绍
1.1 为什么使用EasyExcel?
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
写的内容大致和阅读官方文档差不多
2. 开始使用
2.1 引入jar
<!--easyexcel插件 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.4</version>
</dependency>
<!--lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.2 实体类
根据自己的情况自定义表格导入导出实体类
package top.gmaya.demo.excelentity;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
import java.util.Date;
/**
* @ Description : 用户表格专用实体
* @ Author : GMaya
* @ CreateDate : 2020/1/16 9:13
* @ Version : 1.0
*/
@Data
public class UserExcel{
/** 生成报表时忽略,不生成次字段 */
@ExcelIgnore
private Long id;
/** 定义表头名称和位置,0代表第一列 */
@ExcelProperty(value = "用户名称",index = 0)
private String userName;
@ExcelProperty(value = "用户年龄",index = 1)
private int userAge;
@ExcelProperty(value = "用户昵称",index = 2)
private String userNick;
@ExcelProperty(value = "用户地址",index = 3)
private String userAddress;
/** 指定列宽 */
@ColumnWidth(20)
/** 转化时间 */
@DateTimeFormat(value = "yyyy-MM-dd")
@ExcelProperty(value = "用户生日",index = 4)
private Date userBrithday;
}
2.3 ExcelUtil工具类
根据实际情况封装成工具类,我就写了俩
package top.gmaya.demo.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
/**
* @ Description : excel工具类
* @ Author : GMaya
* @ CreateDate : 2020/1/16 9:35
* @ Version : 1.0
*/
public class ExcelUtil {
/**
* 导出文件 到浏览器
* @param response 响应请求
* @param excelName excel名称
* @param sheetName sheet页面名称
* @param clazz 要转换的实体类类型
* @param data 要导出的数据
* @throws Exception 异常
*/
public static void export2Web(HttpServletResponse response, String excelName, String sheetName, Class clazz, List data) throws Exception {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
excelName = URLEncoder.encode(excelName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
}
/**
* 读取Excel表格
* @param excel 文件
* @param head 实体类映射
* @param readListener 模板的读取类
* @throws Exception
*/
public static void readExcel(MultipartFile excel, Class head,ReadListener readListener) throws Exception{
EasyExcel.read(excel.getInputStream(),head,readListener).sheet().doRead();
}
}
2.4 模板读取类
创建模板读取类, 这个是表格导入的时候需要的,表格中的数据将在这个类里面单独处理,根据类里面设置的数值进行存库,也就是10w条数据,每当1000条就存一下表,方便内存回收。 不然一次性读10w,要占多少。。。
实际情况将下面的构造方法注释打开,相当于你在controller中调用,然后将Service传进来就ok了。
package top.gmaya.demo.excelentity;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* @ Description : 模板的读取类
* @ Author : GMaya
* @ CreateDate : 2020/1/16 10:43
* @ Version : 1.0
*/
// 有个很重要的点 UserListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j public class UserListener extends AnalysisEventListener<UserExcel> {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<UserExcel> list = new ArrayList<>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
// private UserService userService;
/* public UserListener(){
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
userService = new UserService();
}*/
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param userService
*/
/* public UserListener(UserService userService) {
this.userService = userService;
}*/
/**
* 这个每一条数据解析都会来调用
* @param data
* @param context
*
*/
@Override
public void invoke(UserExcel data, AnalysisContext context) {
log.info("解析到一条数据:{}", data.getUserName());
list.add(data);
if (list.size() >= BATCH_COUNT) {
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
saveData();
// 存储完成清理 list
list.clear();
}
}
@Override public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", list.size());
// userService.saveBatch(list);
log.info("存储数据库成功!");
}
}
2.5 controller测试
package top.gmaya.demo.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import top.gmaya.demo.entity.User;
import top.gmaya.demo.excelentity.UserExcel;
import top.gmaya.demo.excelentity.UserListener;
import top.gmaya.demo.util.ExcelUtil;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @ Description : 测试
* @ Author : GMaya
* @ CreateDate : 2020/1/15 16:55
* @ Version : 1.0
*/
@Slf4j @RestController public class DemoController {
/**
* 导出excel
* @param response
*/
@GetMapping("/export2Web") public void export2Web(HttpServletResponse response, String id) {
// 模拟数据
List<User> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
User user = new User((long) i, "张三" + i, i, "小三" + i, "杭州" + i, new Date());
list.add(user);
}
try {
ExcelUtil.export2Web(response, "用户表", "用户信息", UserExcel.class, list);
} catch (Exception e) {
log.error("报表导出异常:", e);
}
}
/**
* 导入excel
* @param file
* @return
*/
@PostMapping("/exportImport") public String exportImport(MultipartFile file) {
try {
ExcelUtil.readExcel(file, UserExcel.class, new UserListener());
} catch (Exception e) {
e.printStackTrace();
}
return "succeed";
}
}
3. 测试
3.1 导出测试
启动项目,浏览器访问接口
直接弹框提示保存位置。保存查看里面内容
3.2 导入测试
使用postman测试文件导入
key:Content-Type
value:multipart/form-data
来源:CSDN
作者:GMaya
链接:https://blog.csdn.net/gfl1427097103/article/details/104003360