基于阿里的EasyExcel早期版本中行数据对应不上的问题解析

前言

开发中,对于开发者而言跟Excel打交道似乎是无可避免的,特别是代码和Excel之间数据的交互上。现在主流的poi工具有很多,但是在性能上、易用性上或多或少会存在一些小小的不足,于是alibaba开源工具easyexcel诞生了。
在使用easyexcel的时候,自己也遇到了一些坑,自己百度了很久都没人提到这个问题,于是自己看着源码一步一步解决了这个问题,同时也在这分享出来参考!

实战

1、导入一个相关依赖即可!

        
            com.alibaba
            easyexcel
            2.0.0-beta6
        

2、读取(上传)Excel文件,使用上非常的简单,只要几行代码。

public class EasyExcelDemo {

    public static void main(String[] args) {
        String path = "E:\\tmp\\demo.xlsx";
        // 自定义读取每一行数据的事件监听
        ReadListen readListen = new ReadListen();
        // 开始读取Excel数据
        ExcelReader reader = EasyExcel.read(path, readListen).build();
        // 读取第一个sheet,readSheet默认是读取第一个,可以自定义
        ReadSheet readSheet = EasyExcel.readSheet().build();
        // 开始读取数据
        reader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        reader.finish();
    }

}

3、自定义ReadListen,主要是为了装数据,方便获取和使用,具体怎么调到这个方法的,以及出现的问题点在哪,往下走。。。

public class ReadListen extends AnalysisEventListener {

    private List sheetData = new ArrayList<>(10);

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        sheetData.add(t);
        System.out.println(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    // Excel数据解析完成后,想要实现的业务逻辑
        System.out.println("excel data resolve finish!!!");
    }
}

4、开始源码分析,easyexcel怎么读取数据,以及问题点分析解决!
4.1 当调用reader.read(readSheet);时,进入

    public ExcelReader read(ReadSheet readSheet) {
        checkFinished();
       // 开始进入解析sheet
        excelAnalyser.analysis(readSheet);
        return this;
    }

我们只要分析excelAnalyser.analysis(readSheet);,进入继续

analysisContext.currentSheet(excelExecutor, readSheet);
try {
// 真正解析sheet表格的地方
    excelExecutor.execute();
} catch (ExcelAnalysisStopException e) {
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Custom stop!");
    }
}
// 这个是在sheet表格解析完成后回调的方法,参考前面自定义的ReadListen
analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext);

点击进入

@Override
    public void execute() {
        parseXmlSource(sheetMap.get(analysisContext.readSheetHolder().getSheetNo()),
        // 这个就是我们真正关心的地方,每行解析完成后会执行的操作
            new XlsxRowHandler(analysisContext, stylesTable));
    }

点击进入,继续

    public XlsxRowHandler(AnalysisContext analysisContext, StylesTable stylesTable) {
    	// 这个是每个单元格执行的一些操作,包括回调之类的
        this.cellHandlers = XlsxHandlerFactory.buildCellHandlers(analysisContext, stylesTable);
       // ...
    }

点击进去,这时我们会注意到notifyEndOneRow,这个正是需要关注的点,继续

    @Override
    public void endHandle(String name) {
        analysisContext.readSheetHolder()
        // 这个就是每一行表格数据解析完成后会触发的回调
            .notifyEndOneRow(new EachRowAnalysisFinishEvent(rowResultHandler.getCurRowContent()), analysisContext);
        rowResultHandler.clearResult();
    }

点击继续,看看里面做了什么操作。。。

  @Override
    public void notifyEndOneRow(AnalysisFinishEvent event, AnalysisContext analysisContext) {
	// ...to do something

        if (rowIndex >= headRowNumber) {
            // Now is data
            for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {
                try {
                    // 真正触发回调的地方,这里会有多个监听,包括我们前面自定义的ReadListen 
                    readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext);
           // ...
             

现在慢慢的明朗了整个读取流程,在debug之后,看看每个Listenner之后,找到一个ModelBuildEventListener,这个就是Excel数据转换的Listenner,也就是前面说到的会导致Excel表格列数据为空的时候,导致位置发生变化的地方,具体看看这里做了什么?

private Object buildStringList(Map cellDataMap, ReadHolder currentReadHolder,
        AnalysisContext context) {
        // 这里是为了兼容一些旧的代码,做了一个映射,是返回list还是map
        if (context.readWorkbookHolder().getDefaultReturnMap()) {
           // ...省略不需要关心的代码
            return map;
        } else {
            List list = new ArrayList();
            for (Map.Entry entry : cellDataMap.entrySet()) {
             // ...省略不需要关心的代码
            return list;
        }
    }

在进入context.readWorkbookHolder().getDefaultReturnMap())的时候,在新的版本中默认是true,这样返回map的时候就不会出现前面说的列数据对不上的问题。下面看看返回Map和List有什么区别。
基于阿里的EasyExcel早期版本中行数据对应不上的问题解析_第1张图片
使用List返回的数据示例:
List示例
使用Map返回的数据示例:
Map示例
通过对比,我们就很快可以发现使用List缺少了序号而导致无法把数据对应上,而使用Map的时候,key就是序号,value是我们需要的单元格数据。
那么,这个开关在哪里呢?细心的应该看到了,如果你也存在这个问题,又不想升级版本等,可以使用一个开关设置成Map再转换成自己需要的List集合即可。或者说不想用Map,那么都可以自己设置。

    public static void main(String[] args) {
		// ...省略
        ReadSheet readSheet = EasyExcel.readSheet().build();
        // 具体的设置开关,新的的版本中默认true
        reader.analysisContext().readWorkbookHolder().setDefaultReturnMap(true);
        // 开始读取数据
        reader.read(readSheet);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        reader.finish();
    }

在前面的代码中,添加一句reader.analysisContext().readWorkbookHolder().setDefaultReturnMap(true);,注意这里默认就是true,可以设置为false,返回的数据就是list了。

最后

easyExcel用起来确实很方便,避免了自己去写一大堆代码,性能也不可靠,但是它再使用的时候可能还是存在一些坑,这个就需要自己去解决,由于是国人写的代码,看起来也很简单,相信大部分问题都是可以在读取源码之后解决的,踩坑不可怕,可怕的是踩坑之后不知道怎么爬出来。这里是写给所有人看的,也是写给自己看的。这不仅仅解决了一个问题,更是提供了一个思路。

你可能感兴趣的:(功能)