一、布景
最近在做Execl数据导入的需求的时候发现团队这边Excel东西类完成方法都不相同,有的根据poi完成,有的是根据easyexcel完成,并且封装的方法也不相同,没有一个一致的运用方法,所以根据该问题对Execl操作东西进行了调研。
二、常用框架比照
业界常用的框架有POI和EasyExecl两个框架,履行流程图比照如下:
POI
对照上面的流程图,当利用POI去读取Excel时,首先会将数据悉数加载到内存中,然后返回给调用者,当数据量比较大时,及其容易发生OOM。
EasyExecl
对照上面的流程图,与POI不必的是,EasyExcel主要是采用sax形式一行一行解析,并将一行的解析成果以观察者的形式告诉处理,即便数据量较大时也不会发生OOM。
三、EasyExecl完成原理
官网地址:easyexcel.opensource.alibaba.com/
四、EasyExecl用法
参考官网API:
easyexcel.opensource.alibaba.com/docs/curren…
五、EasyExecl问题
1、每读取一个不同的Execl需要定义不同的监听器,运用起来比较繁琐
2、写入的时候原生代码比较零碎,对动态列,多sheet写运用起来代码复杂度高。
六、EasyExecl增强计划
1、规划原则
读取:Consumer+盘绕履行形式
写入:自定义函数式接口
2、完成方法
2.1、单Sheet读
public class ExcelConsumerListener<T> extends AnalysisEventListener<T> { private Consumer<List<T>> consumer; private List<T> list; @Overridepublicvoidinvoke(Tdata,AnalysisContextanalysisContext){ if(Objects.isNull(data)){ return; } //一切特点为空是模板过错或许空表格 if(BeanUtils.checkAllFieldIsNULL(data)){ throw new RuntimeException("Excel内容存在过错"); } list.add(data); if (list.size() >= segmentSize) { consumer.accept(list); list.clear(); } }
@Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { if(CollectionUtils.isEmpty(list)){ return; } consumer.accept(list); list.clear(); }}
2.2、多Sheet读
public class MultiSheetExcelConsumerListener<T> extends AnalysisEventListener<T> { @Override public void invoke(T data, AnalysisContext analysisContext) { if(Objects.isNull(data)){ return; } //一切特点为空是模板过错或许空表格 if(BeanUtils.checkAllFieldIsNULL(data)){ throw new RuntimeException("Excel内容存在过错"); } list.add(data); if (list.size() >= segmentSize) { dealConsumer(analysisContext); } }
@Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { if(CollectionUtils.isEmpty(list)){ return; } dealConsumer(analysisContext); }
private void dealConsumer(AnalysisContext analysisContext){ MultiSheet<T> multiSheet=new MultiSheet<>(); multiSheet.setList(list); multiSheet.setSheetNo(analysisContext.readSheetHolder().getSheetNo()); multiSheet.setSheetName(analysisContext.readSheetHolder().getSheetName()); consumer.accept(multiSheet); list.clear(); }}
2.3、动态列读
public class NoModelExcelConsumerListener<T> extends AnalysisEventListener<Map<Integer,String>> { @Override public void invokeHeadMap(Map headMap, AnalysisContext context) { this.headMap=headMap; }
@Override public void invoke(Map<Integer,String> data, AnalysisContext analysisContext) { if(Objects.isNull(data)||data.isEmpty()){ return; } if(Objects.isNull(headMap)||headMap.isEmpty()){ return; } //一切特点为空是模板过错或许空表格 if(BeanUtils.checkAllFieldIsNULL(data)){ throw new RuntimeException("Excel内容存在过错"); } //组装数据目标 Map<String,String> map=new HashMap<>(); headMap.forEach((k, v) -> { map.put(v,data.get(k)); }); list.add(map); if (list.size() >= segmentSize) { consumer.accept(list); list.clear(); } }
@Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { if(CollectionUtils.isEmpty(list)){ return; } consumer.accept(list); }}
2.4、写入
@FunctionalInterfacepublic interface ExcelWrite { /** * 写入 * @param excelWriter * @param writeSheet */ void doWrite(ExcelWriter excelWriter, WriteSheet writeSheet);}
七、EasyExecl增强后运用
1、读
//指定分片大小读取,最大支持2000,即segmentList大小为30ExcelUtil.read("D://20200903.xlsx", ExcelDataDTO.class,30,segmentList ->{ //对读取到的数据进行事务处理 }).sheet().doRead();
//动态列读取ExcelUtil.noModelRead("D://assign-apply.xlsx",2,segmentList ->{ //对读取到的数据进行事务处理}).sheet().doRead();
//多sheet读取InputStream inputStream=new FileInputStream("/Users/xxx/abc.xlsx");ExcelUtil.readMultiSheet(inputStream,XxxxDTO.class,multiSheet->{ List<XxxxDTO> list=multiSheet.getList(); if(CollectionUtils.isEmpty(list)){ return;}}).doReadAll();
2、写
//testWrite为excel里sheet的名称ExcelUtil.write("D://20200903.xlsx","testWrite",ExcelDataDTO.class,(excelWriter,writeSheet) -> { //查询数据list进行excel写入,假如list数据很大能够进行循环写入 excelWriter.write(list,writeSheet);});
//动态列写入//testWrite为excel里sheet的名称ExcelUtil.noModelWrite("D://20210510.xlsx","testWrite",head(),(excelWriter,writeSheet) -> { //查询数据list进行excel写入,假如list数据很大能够进行循环写入cd excelWriter.write(dataList(),writeSheet);});
//多sheet不同目标写ExcelUtil.write("/Users/xxx/20200903.xlsx","testWrite",ExcelDataDTO.class,(excelWriter,writeSheet)->{ WriteSheet test1 = EasyExcel.writerSheet(0, "test1").head(ExcelDataDTO.class).build(); excelWriter.write(list1,test1);
WriteSheettest2=EasyExcel.writerSheet(1,"test2").head(ExcelDataOtherDTO.class).build(); excelWriter.write(list2,test2);});
参考资料
原文链接https://mp.weixin.qq.com/s?__biz=MzU2NzY1Mzk2Mw==&mid=2247483683&idx=1&sn=830a0ba36d1fa462d283a8a4d9640b05&chksm=fc98a207cbef2b11f1fdd1129718b75eea7672d84eec1548666bbdda1523256b5895b8edf7f4&token=417195537&lang=zh_CN#rd