0.easyexcel引入

我现在引入的是此时的最新版

com.alibaba easyexcel 3.0.5


1.准备excel表格

此处,演示3个不同类型的字段

字符串

日期

小数

F1

F2

F3

1

2022-05-13 12:34:56

1.23

2

2022-05-13 13:34:56

1.24

3

2022-05-13 14:34:56

1.25

4

2022-05-13 15:34:56

1.26

2.创建导入数据接收的对象

package com.example.support.entity; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * @author LWB * @Description 读excel */ @Data public class ExcelReadDTO { @ExcelProperty("F1") private String str; @ExcelProperty("F2") private Date time; //读取excel中对应的字段名,也可以用列索引去读取(使用index属性指定),但不建议,因为列可能会变动,建议使用这种和列实际名无关的标记字段来读取 @ExcelProperty("F3") private BigDecimal money; }


3.创建读取监听器

package com.example.support.excel.listener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.listener.ReadListener; import com.example.global.HttpHolder; import com.example.service.ExcelService; import com.example.support.entity.ExcelReadDTO; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; /** * @author LWB * @Description excel读取的监听器 * 有个很重要的点 ExcelReadListener监听器 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 * 所以,如果需要传入spring管理的对象,要么按照文档中的说明,直接使用构造器传参进来,要么使用ThreadLocal线程级别的变量工具传参 * 实际应用中,可能会需要传入很多的spring管理的对象,或者其他上下文参数,遇到这种情况,建议使用ThreadLocal来传递参数,简单,便捷,无需重复修改构造器 * 如果是简单的传递个别上下文参数,也可以选用构造器传参的方式 */ @Slf4j public class ExcelReadListener implements ReadListener { private ExcelService excelService; /** * 不适用构造器传入,使用threadLocal工具传入 * 适用于读取监听器中,需要使用多个上下文参数的情况 */ public ExcelReadListener(){ this.excelService = HttpHolder.get("excelService",ExcelService.class); String p1 = HttpHolder.get("p1", String.class); String p2 = HttpHolder.get("p2", String.class); } /** * 构造器中传入spring管理的对象 * 适用于传入较少上下文参数的情况 * @param excelService */ public ExcelReadListener(ExcelService excelService){ this.excelService = excelService; } /** * 每隔N条存储数据库,实际使用中可以1000条或者更多,然后清理cachedDataList ,方便内存回收 * 如果表格数据量很大,成千上万条,可以分批进行入库,数量不多的话,没必要做分配 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List cachedDataList = new ArrayList<>(BATCH_COUNT); /** 终止读取,提示前端的信息 */ @Getter private String stopMsg; /** * 这个方法,每一条数据解析都会来调用 * @param data * @param context */ @Override public void invoke(ExcelReadDTO data, AnalysisContext context) { log.info("正在读取:"+ data.toString()); // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM /* if (cachedDataList.size() >= BATCH_COUNT) { //保存 excelService.saveData(); // 存储完成清理 list cachedDataList.clear(); }*/ } /** * 所有数据解析完成了 会来调用 * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("读取完成"); //若进行分批处理,在读取完成后,还要检查下 cachedDataList 中是否还有剩余未入库的数据,因为如果没达到缓存最大值,invoke方法不会进行存储 //if(cachedDataList.size() > 0) excelService.saveData(); } }

4.controller接口层的逻辑

/** * 读取excel的第一个sheet * @param excelFile * @return */ @PostMapping("readSheet0") @SneakyThrows public Ret readSheet0(MultipartFile excelFile){ //看文档(最简单的读)中,可以了解,一共有5中读取数据的写法,个人使用下来,第3种最为简洁明了,层次清晰 //其中,EasyExcel.read方法,使用了readListener对象的参数的方法,有6种,可以根据不同需求使用 // 1. read(File file, ReadListener readListener) // 2. read(File file, Class head, ReadListener readListener) // 3. read(String pathName, ReadListener readListener) // 4. read(String pathName, Class head, ReadListener readListener) // 5. read(InputStream inputStream, ReadListener readListener) // 6. read(InputStream inputStream, Class head, ReadListener readListener) //常规情况下,spring接口收到文件的类型是MultipartFile,因此,我们直接使用该对象的getInputStream()方法获取输入流 //这里 需要指定读用哪个class去读,文件流会自动关闭 //监听器构造方法1:构造器中传入spring管理的对象 ExcelReadListener excelReadListener = new ExcelReadListener(excelService); //监听器构造方法2:使用无参构造器,传入多个上下文参数给读取监听器 //ExcelReadListener excelReadListener = new ExcelReadListener(); //HttpHolder.set("excelService",excelService); //HttpHolder.set("p1",p1); //HttpHolder.set("p2",p2); EasyExcel.read(excelFile.getInputStream(), ExcelReadDTO.class,excelReadListener) .sheet()//指定读取哪个sheet .headRowNumber(2)//指定标题行(需要) .doRead();//执行读取 //headRowNumber值的解释: //0 - This Sheet has no head ,since the first row are the data 没有标题 //1 - This Sheet has one row head , this is the default 第一行,默认 //2 - This Sheet has two row head ,since the third row is the data 第2行 return Ret.success(); }

控制台输出如下: 2022-05-13 16:57:59.582 INFO 18028 --- [io-54321-exec-2] c.e.s.excel.listener.ExcelReadListener : 正在读取:ExcelReadDTO(str=1, time=Fri May 13 12:34:56 CST 2022, money=1.23) 2022-05-13 16:57:59.583 INFO 18028 --- [io-54321-exec-2] c.e.s.excel.listener.ExcelReadListener : 正在读取:ExcelReadDTO(str=2, time=Fri May 13 13:34:56 CST 2022, money=1.24) 2022-05-13 16:57:59.583 INFO 18028 --- [io-54321-exec-2] c.e.s.excel.listener.ExcelReadListener : 正在读取:ExcelReadDTO(str=3, time=Fri May 13 14:34:56 CST 2022, money=1.25) 2022-05-13 16:57:59.583 INFO 18028 --- [io-54321-exec-2] c.e.s.excel.listener.ExcelReadListener : 正在读取:ExcelReadDTO(str=4, time=Fri May 13 15:34:56 CST 2022, money=1.26) 2022-05-13 16:57:59.583 INFO 18028 --- [io-54321-exec-2] c.e.s.excel.listener.ExcelReadListener : 读取完成

5.关于ThreadLocal的使用

以后再开辟时间进行演示介绍,这是个很有用的工具,让人又爱又恨,需要小心使用

6.后期预告

接下来,我将演示

  • 如何在导入时进行字段的转换,
  • 导入时进行数据校验,并将错误提示前端
  • 额外信息的读取,如批注,超链接,合并单元格的信息
  • 多sheet导入