springboot整合easy excel
1、环境准备
创建springboot项目,导入jar包
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version><!--$NO-MVN-MAN-VER$-->
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
2、目录结构
3、撸代码
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
//@Accessors(chain = true)
public class DemoData {
private String equNo;
private String equName;
private String ipAddress;
}
模拟DAO
import java.util.List;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
监听回调demo
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
/**
* 模板的读取类
*/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 50;
//使用内存可见性,可以让外面获取到list中的内容
public volatile List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
上传excel监听类
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
/**
* 模板的读取类
*/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class UploadDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER =
LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO uploadDAO;
public UploadDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
uploadDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param uploadDAO
*/
public UploadDataListener(DemoDAO uploadDAO) {
this.uploadDAO = uploadDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
uploadDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
测试类
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.alibaba.excel.EasyExcel;
public class TestExcel {
/**
* 最简单的读
* <p>1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>3. 直接读即可
* 注意:DemoData使用Lombok生成构造方法时,添加了有参构造一定要添加无参构建注解,否则会抛出异常:
* com.alibaba.excel.exception.ExcelAnalysisException:
* Can not instance class: com.study.testEasyExcel.test.DemoData
* 再次说明:在实体类上不能使用Lombok的@Accessors(chain = true)注解,否则会解析不到数据
*/
@Test
public void simpleRead() {
String fileName = "C:\\test\\设备模板.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
DemoDataListener ddl = new DemoDataListener();
EasyExcel.read(fileName, DemoData.class, ddl).sheet().doRead();
//使用内存可见性获取excel中的数据,写得玩一下,实际生产中作用不大
List<DemoData> list = ddl.list;
System.out.println(list);
}
/**
* 最简单的写
* <p>1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
* <p>2. 直接写即可
*/
@Test
public void simpleWrite() {
String fileName = "C:\\test\\设备模板1.xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class).sheet("sheet1").doWrite(data());
}
public static List<DemoData> data() {
List<DemoData> list = new ArrayList<>();
list.add(new DemoData("aaa","aaa","1.1.1.1"));
list.add(new DemoData("bbb","bbb","1.1.1.2"));
list.add(new DemoData("ccc","ccc","1.1.1.3"));
return list;
}
}
上传下载handler
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.excel.EasyExcel;
import com.study.testEasyExcel.test.DemoData;
import com.study.testEasyExcel.test.UploadDataListener;
@Controller
public class OperateHandler {
@GetMapping("/index")
public String index() {
return "index";
}
@GetMapping("/success")
public String success() {
return "success";
}
@GetMapping("/downLoad")
public void downLoadExcel(HttpServletResponse resp) throws Exception {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
resp.setContentType("application/vnd.ms-excel");
resp.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
resp.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(resp.getOutputStream(), DemoData.class).sheet("模板").doWrite(data());
}
/**
* 文件上传
* <p>1. 创建excel对应的实体对象 参照{@link UploadData}
* <p>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
* <p>3. 直接读即可
*/
@PostMapping("/upload")
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), DemoData.class, new UploadDataListener()).sheet().doRead();
return "success";
}
public static List<DemoData> data() {
List<DemoData> list = new ArrayList<>();
list.add(new DemoData("aaa","aaa","1.1.1.1"));
list.add(new DemoData("bbb","bbb","1.1.1.2"));
list.add(new DemoData("ccc","ccc","1.1.1.3"));
return list;
}
}
index界面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>测试界面</h3>
<a th:href="@{/downLoad}">下载Excel文件</a>
<br />
<label th:text="上传文件"></label>
<form method="post" th:action="@{/upload}" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<button type="submit">上传</button>
</form>
</body>
</html>
success界面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>上传成功</h1> </body> </html>
4、启动测试
运行springboot项目,在浏览器中测试
5、注意事项:
1、实体类使用Lombok生成构造方法时,添加了有参构造注解之后一定要添加无参构建注解,否则会抛出异常:
com.alibaba.excel.exception.ExcelAnalysisException:Can not instance class:
2、在实体类上不能使用Lombok的@Accessors(chain = true)注解,否则会解析不到数据
参考:https://github.com/alibaba/easyexcel
源码:https://gitee.com/i_have/testEasyExcel