一、布景

最近在做Execl数据导入的需求的时候发现团队这边Excel东西类完成方法都不相同,有的根据poi完成,有的是根据easyexcel完成,并且封装的方法也不相同,没有一个一致的运用方法,所以根据该问题对Execl操作东西进行了调研。

二、常用框架比照

业界常用的框架有POI和EasyExecl两个框架,履行流程图比照如下:

POI

EasyExcel拓展,让你的Execl操作更加简单

EasyExcel拓展,让你的Execl操作更加简单

对照上面的流程图,当利用POI去读取Excel时,首先会将数据悉数加载到内存中,然后返回给调用者,当数据量比较大时,及其容易发生OOM。

EasyExecl

EasyExcel拓展,让你的Execl操作更加简单

EasyExcel拓展,让你的Execl操作更加简单

对照上面的流程图,与POI不必的是,EasyExcel主要是采用sax形式一行一行解析,并将一行的解析成果以观察者的形式告诉处理,即便数据量较大时也不会发生OOM。

三、EasyExecl完成原理

官网地址:easyexcel.opensource.alibaba.com/

EasyExcel拓展,让你的Execl操作更加简单

四、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