springboot整合easyexcel
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)注解,否则会解析不到数据