Easyexcel(2-文件读取)

打印 上一主题 下一主题

主题 978|帖子 978|积分 2934

同步读取

读取单个Sheet


  • 通过sheet方法指定对应的Sheet名称或下标读取文件信息
  • 通过doReadSync方法实现同步读取
  1. @Data
  2. public class UserExcel {
  3.     @ExcelIgnore
  4.     private Integer id;
  5.     @ExcelProperty(index = 0, value = "姓名")
  6.     private String name;
  7.     @ExcelProperty(index = 1, value = "年龄")
  8.     private Integer age;
  9.     @DateTimeFormat(value = "yyyy-MM-dd")
  10.     @ExcelProperty(index = 2, value = "出生日期")
  11.     private Date birthday;
  12. }
复制代码
  1. @RestController
  2. public class Test02Controller {
  3.     /**
  4.      * 上传单个文件, 同步读取excel文件
  5.      */
  6.     @PostMapping("/uploadFile")
  7.     public void uploadFile(MultipartFile file) {
  8.         try (InputStream in = file.getInputStream()) {
  9.             List<UserExcel> userExcelList = EasyExcel.read(in)
  10.                     // 读取第一个sheet
  11.                     .sheet(0)
  12.                     // 如果第一行才是标题,第二行是数据,从第二行开始读取
  13.                     .headRowNumber(1)
  14.                     .head(UserExcel.class)
  15.                     .doReadSync();
  16.             for (UserExcel userExcel : userExcelList) {
  17.                 System.out.println(userExcel);
  18.             }
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         }
  22.     }
  23. }
复制代码
读取多个Sheet(同一个对象)

使用doReadAllSync方法读取所有Sheet,实用于每个Sheet的对象都同等的情况
  1. @PostMapping("/uploadFile2")
  2. public void uploadFile2(MultipartFile file) {
  3.     try (InputStream in = file.getInputStream()) {
  4.         List<UserExcel> userExcelList = EasyExcel.read(in)
  5.                 // 如果第一行才是标题,第二行是数据,从第二行开始读取
  6.                 .headRowNumber(1)
  7.                 .head(UserExcel.class)
  8.                 .doReadAllSync();
  9.         for (UserExcel userExcel : userExcelList) {
  10.             System.out.println(userExcel);
  11.         }
  12.     } catch (Exception e) {
  13.         e.printStackTrace();
  14.     }
  15. }
复制代码
读取多个Sheet(不同对象)

当每个Sheet的对象不同等的情况下,使用doReadAllSync方法无法指定每个Sheet的对象,可以依次读取Sheet进行解析
注意:依次读取Sheet会出现重复读取流对象的情况,而一个流对象只能读取一次,重复使用会导致非常
  1. @PostMapping("/uploadFile4")
  2. public void uploadFile4(MultipartFile file) {
  3.     InputStream in = null;
  4.     try {
  5.         in = file.getInputStream();
  6.         List<UserExcel> userExcelList1 = EasyExcel.read(in)
  7.                 // 读取第一个sheet
  8.                 .sheet(0)
  9.                 // 如果第一行才是标题,第二行是数据,从第二行开始读取
  10.                 .headRowNumber(1)
  11.                 .head(UserExcel.class)
  12.                 .doReadSync();
  13.         // 读取剩余的sheet
  14.         in = file.getInputStream();
  15.         List<UserExcel> userExcelList2 = EasyExcel.read(in)
  16.                 .sheet(1)
  17.                 // 如果第一行才是标题,第二行是数据,从第二行开始读取
  18.                 .headRowNumber(1)
  19.                 .head(UserExcel.class)
  20.                 .doReadSync();
  21.         List<UserExcel> userExcelList = new ArrayList<>();
  22.         userExcelList.addAll(userExcelList1);
  23.         userExcelList.addAll(userExcelList2);
  24.         for (UserExcel userExcel : userExcelList) {
  25.             System.out.println(userExcel);
  26.         }
  27.     } catch (Exception e) {
  28.         e.printStackTrace();
  29.     } finally {
  30.         try {
  31.             if (in != null) {
  32.                 in.close();
  33.             }
  34.         } catch (Exception e) {
  35.             e.printStackTrace();
  36.         }
  37.     }
  38. }
复制代码
异步读取

监听器

查看监听器源码,通过实现ReadListener接口或继续AnalysisEventListener类可以自定义读取Sheet监听器
  1. public interface ReadListener<T> extends Listener {
  2.    
  3.     // 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则继续读取下一行
  4.     default void onException(Exception exception, AnalysisContext context) throws Exception {
  5.         throw exception;
  6.     }
  7.     // 获取表头数据
  8.     default void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {}
  9.     // 一行行读取表格内容
  10.     void invoke(T data, AnalysisContext context);
  11.     // 读取条额外信息:批注、超链接、合并单元格信息等
  12.     default void extra(CellExtra extra, AnalysisContext context) {}
  13.            // 读取完成后的操作
  14.     void doAfterAllAnalysed(AnalysisContext context);
  15.     // 是否还有数据
  16.     default boolean hasNext(AnalysisContext context) {
  17.         return true;
  18.     }
  19. }
复制代码
非常处理

ExcelDateConvertException

表示数据转换非常错误,出现该非常时会继续解析文件信息
  1. public abstract class AnalysisEventListener<T> implements ReadListener<T> {
  2.     // 解析表头数据
  3.     @Override
  4.     public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
  5.         invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
  6.     }
  7.     public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {}
  8. }
复制代码
ExcelAnalysisStopException

非数据转换非常错误,在onexcetpion中抛出该非常后停止解析
  1. @Getter
  2. @Setter
  3. @EqualsAndHashCode
  4. public class ExcelDataConvertException extends ExcelRuntimeException {
  5.     private Integer rowIndex;
  6.     private Integer columnIndex;
  7.     private CellData<?> cellData;
  8.     private ExcelContentProperty excelContentProperty;
  9.     public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData<?> cellData,
  10.         ExcelContentProperty excelContentProperty, String message) {
  11.         super(message);
  12.         this.rowIndex = rowIndex;
  13.         this.columnIndex = columnIndex;
  14.         this.cellData = cellData;
  15.         this.excelContentProperty = excelContentProperty;
  16.     }
  17.     public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData<?> cellData,
  18.         ExcelContentProperty excelContentProperty, String message, Throwable cause) {
  19.         super(message, cause);
  20.         this.rowIndex = rowIndex;
  21.         this.columnIndex = columnIndex;
  22.         this.cellData = cellData;
  23.         this.excelContentProperty = excelContentProperty;
  24.     }
  25. }
复制代码
读取单个Sheet(不指定对象)

读取文件时使用doRead方法进行异步操作,同时指定对应的监听器解析文件数据
Map中的key表示列号、value表示数据
  1. public class ExcelAnalysisStopException extends ExcelAnalysisException {
  2.     public ExcelAnalysisStopException() {}
  3.     public ExcelAnalysisStopException(String message) {
  4.         super(message);
  5.     }
  6.     public ExcelAnalysisStopException(String message, Throwable cause) {
  7.         super(message, cause);
  8.     }
  9.     public ExcelAnalysisStopException(Throwable cause) {
  10.         super(cause);
  11.     }
  12. }
复制代码
  1. public class UserExcelListener1 extends AnalysisEventListener<Map<Integer, String>> {
  2.     Logger log = LoggerFactory.getLogger(getClass());
  3.     private List<Map<Integer, String>> userExcelList = new ArrayList<>();
  4.     @Override
  5.     public void invoke(Map<Integer, String> map, AnalysisContext analysisContext) {
  6.         log.info("解析到一条数据:{}", JSON.toJSONString(map));
  7.         userExcelList.add(map);
  8.     }
  9.     @Override
  10.     public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  11.         log.info("已解析完所有数据!");
  12.         userExcelList.clear();
  13.     }
  14.     @Override
  15.     public void onException(Exception exception, AnalysisContext context) throws Exception {
  16.         if (exception instanceof ExcelDataConvertException) {
  17.             ExcelDataConvertException convertException = (ExcelDataConvertException) exception;
  18.             Integer row = convertException.getRowIndex();
  19.             log.error("第{}行数据转换失败,异常信息:{}", row, exception.getMessage());
  20.         } else {
  21.             log.error("导入其他异常信息:{}", exception.getMessage());
  22.         }
  23.     }
  24.     public List<Map<Integer, String>> getUserExcelList() {
  25.         return userExcelList;
  26.     }
  27.     public void setUserExcelList(List<Map<Integer, String>> userExcelList) {
  28.         this.userExcelList = userExcelList;
  29.     }
  30. }
复制代码
读取单个Sheet(指定对象)
  1. @PostMapping("/uploadFile1")
  2. public void uploadFile1(MultipartFile file) {
  3.     try (InputStream in = file.getInputStream()) {
  4.         UserExcelListener1 listener = new UserExcelListener1();
  5.         EasyExcel.read(in, listener)
  6.                 .sheet(0)
  7.                 .headRowNumber(1) // 第一行是标题, 从第二行开始读取
  8.                 .doRead();
  9.     } catch (Exception e) {
  10.         e.printStackTrace();
  11.     }
  12. }
复制代码
  1. public class UserExcelListener extends AnalysisEventListener<UserExcel> {
  2.     Logger log = LoggerFactory.getLogger(getClass());
  3.     private List<UserExcel> userExcelList = new ArrayList<>();
  4.     @Override
  5.     public void invoke(UserExcel userExcel, AnalysisContext analysisContext) {
  6.         log.info("解析到一条数据:{}", JSON.toJSONString(userExcel));
  7.         userExcelList.add(userExcel);
  8.     }
  9.     @Override
  10.     public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  11.         log.info("已解析完所有数据!");
  12.         userExcelList.clear();
  13.     }
  14.     @Override
  15.     public void onException(Exception exception, AnalysisContext context) throws Exception {
  16.         if (exception instanceof ExcelDataConvertException) {
  17.             ExcelDataConvertException convertException = (ExcelDataConvertException) exception;
  18.             Integer row = convertException.getRowIndex();
  19.             log.error("第{}行数据转换失败,异常信息:{}", row, exception.getMessage());
  20.         } else {
  21.             log.error("导入其他异常信息:{}", exception.getMessage());
  22.         }
  23.     }
  24.     public List<UserExcel> getUserExcelList() {
  25.         return userExcelList;
  26.     }
  27.     public void setUserExcelList(List<UserExcel> userExcelList) {
  28.         this.userExcelList = userExcelList;
  29.     }
  30. }
复制代码
读取多个Sheet


  • 获取Sheet的总数,通过循环遍历的方式指定每个Sheet的监听器进行解析
  • 使用构造器的方式传入Sheet对应的下标,在抛出非常时获取SheetNo和对应的行号,方便进行排查
  1. @PostMapping("/uploadFile5")
  2. public void uploadFile5(MultipartFile file) {
  3.     try (InputStream in = file.getInputStream()) {
  4.         UserExcelListener listener = new UserExcelListener();
  5.         EasyExcel.read(in, UserExcel.class, listener)
  6.                 .sheet(0)
  7.                 .headRowNumber(1) // 第一行是标题, 从第二行开始读取
  8.                 .doRead();
  9.     } catch (Exception e) {
  10.         e.printStackTrace();
  11.     }
  12. }
复制代码
  1. public class UserExcelListener2 extends AnalysisEventListener<UserExcel> {
  2.    
  3.     Logger log = LoggerFactory.getLogger(getClass());
  4.     private Integer sheetNo;
  5.    
  6.     private List<UserExcel> userExcelList = new ArrayList<>();
  7.     public UserExcelListener2(Integer sheetNo) {
  8.         this.sheetNo = sheetNo;
  9.     }
  10.     @Override
  11.     public void invoke(UserExcel userExcel, AnalysisContext analysisContext) {
  12.         log.info("解析到一条数据:{}", JSON.toJSONString(userExcel));
  13.         userExcelList.add(userExcel);
  14.     }
  15.     @Override
  16.     public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  17.         log.info("已解析完所有数据!");
  18.         userExcelList.clear();
  19.     }
  20.     @Override
  21.     public void onException(Exception exception, AnalysisContext context) throws Exception {
  22.         if (exception instanceof ExcelDataConvertException) {
  23.             ExcelDataConvertException convertException = (ExcelDataConvertException) exception;
  24.             Integer row = convertException.getRowIndex();
  25.             log.error("sheetNo:{},第{}行数据转换失败,异常信息:{}", sheetNo, row, exception.getMessage());
  26.         } else {
  27.             log.error("导入其他异常信息:{}", exception.getMessage());
  28.         }
  29.     }
  30.     public List<UserExcel> getUserExcelList() {
  31.         return userExcelList;
  32.     }
  33.     public void setUserExcelList(List<UserExcel> userExcelList) {
  34.         this.userExcelList = userExcelList;
  35.     }
  36. }
复制代码
分批读取(线程池操作)


  • 使用构造器的方式传入Sheet对应的下标和自定义线程池,使用这种分批处理的方式,避免内存的消耗,加速文件的解析入库
  • 数据库入库时可以使用MySQL的批量插入语法,同时指定每次插入数据的大小,相较于MyBatisPlus的批量插入方法较快
  1. @PostMapping("/uploadFile6")
  2. public void uploadFile6(MultipartFile file) {
  3.     try (InputStream in = file.getInputStream();
  4.          ExcelReader build = EasyExcel.read(in).build();) {
  5.         List<ReadSheet> readSheets = build.excelExecutor().sheetList();
  6.         for (int i = 0, len = readSheets.size(); i < len; i++) {
  7.             UserExcelListener2 listener = new UserExcelListener2(i);
  8.             ReadSheet sheet = EasyExcel.readSheet(readSheets.get(i).getSheetNo())
  9.                     .head(UserExcel.class)
  10.                     .headRowNumber(1)
  11.                     .registerReadListener(listener)
  12.                     .build();
  13.             build.read(sheet);
  14.         }
  15.         build.finish();
  16.     } catch (Exception e) {
  17.         e.printStackTrace();
  18.     }
  19. }
复制代码
  1. /**
  2. * UserListener 不能被spring管理,要每次读取excel都要new,
  3. * 然后里面用到spring可以构造方法传进去
  4. */
  5. public class UserExcelListener3 extends AnalysisEventListener<UserExcel> {
  6.     Logger log = LoggerFactory.getLogger(getClass());
  7.     private static final Integer BATCH_SIZE = 1000;
  8.     private Integer sheetNo;
  9.     private Executor executor;
  10.     private List<UserExcel> userExcelList = new ArrayList<>();
  11.     public UserExcelListener3(Integer sheetNo, Executor executor) {
  12.         this.sheetNo = sheetNo;
  13.         this.executor = executor;
  14.     }
  15.     @Override
  16.     public void invoke(UserExcel userExcel, AnalysisContext analysisContext) {
  17.         log.info("解析到一条数据:{}", JSON.toJSONString(userExcel));
  18.         userExcelList.add(userExcel);
  19.         if (userExcelList.size() >= BATCH_SIZE) {
  20.             List<UserExcel> userExcels = BeanUtil.copyToList(userExcelList, UserExcel.class);
  21.             CompletableFuture.runAsync(() -> {
  22.                 // 业务操作
  23.                 // saveToDB(userExcels);
  24.             }, executor);
  25.             userExcelList.clear();
  26.         }
  27.     }
  28.     @Override
  29.     public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  30.         log.info("已解析完所有数据!");
  31.         if (!userExcelList.isEmpty()) {
  32.             List<UserExcel> userExcels = BeanUtil.copyToList(userExcelList, UserExcel.class);
  33.             CompletableFuture.runAsync(() -> {
  34.                 // 业务操作
  35.                 // saveToDB(userExcels);
  36.             }, executor);
  37.             userExcelList.clear();
  38.         }
  39.     }
  40.     @Override
  41.     public void onException(Exception exception, AnalysisContext context) throws Exception {
  42.         if (exception instanceof ExcelDataConvertException) {
  43.             ExcelDataConvertException convertException = (ExcelDataConvertException) exception;
  44.             Integer row = convertException.getRowIndex();
  45.             log.error("sheetNo:{},第{}行数据转换失败,异常信息:{}", sheetNo, row, exception.getMessage());
  46.         } else {
  47.             log.error("导入其他异常信息:{}", exception.getMessage());
  48.         }
  49.     }
  50. }
复制代码
事务操作

当使用监听器读取文件数据,使用分批插入数据的方法时,因为监听器不归Spring管理,以是无法使用Spring的事务注解进行事务的相关操作,怎么保证事务?
可以通过构造器的方式传入事务管理器,手动提交和回滚事务
  1. @PostMapping("/uploadFile7")
  2. public void uploadFile77(MultipartFile file) {
  3.     try (InputStream in = file.getInputStream();
  4.          ExcelReader build = EasyExcel.read(in).build();) {
  5.         ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 60L,
  6.                 TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.AbortPolicy());
  7.         List<ReadSheet> readSheets = build.excelExecutor().sheetList();
  8.         for (int i = 0, len = readSheets.size(); i < len; i++) {
  9.             UserExcelListener3 listener = new UserExcelListener3(i, executor);
  10.             ReadSheet sheet = EasyExcel.readSheet(readSheets.get(i).getSheetNo())
  11.                     .head(UserExcel.class)
  12.                     .headRowNumber(1)
  13.                     .registerReadListener(listener)
  14.                     .build();
  15.             build.read(sheet);
  16.         }
  17.         build.finish();
  18.     } catch (Exception e) {
  19.         e.printStackTrace();
  20.     }
  21. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表