Excel解析工具easyexcel全面探索

本文详细介绍了阿里巴巴的Excel解析工具EasyExcel,对比了它与POI的区别,强调了EasyExcel在内存消耗上的优势。文章分别探讨了EasyExcel的读取和写入操作,包括核心源码解析、监听器实现、读取多页、自定义字段转换、文件上传下载等技巧,并提供了详细的示例和源码分析。
摘要由CSDN通过智能技术生成

1. Excel解析工具easyexcel全面探索

1.1. 简介

之前我们想到Excel解析一般是使用POI,但POI存在一个严重的问题,就是非常消耗内存。所以阿里人员对它进行了重写从而诞生了easyexcel,它解决了过于消耗内存问题,也对它进行了封装让使用者使用更加便利

接下来我先一一介绍它所有的功能细节、如何使用及部分源码解析

1.2. Excel读

1.2.1. 例子

    /**
     * 最简单的读
     * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
     * <p>3. 直接读即可
     */
    @Test
    public void simpleRead() {
        String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    }
  • 官方说明也比较明确,使用简单fileName路径+文件名DemoData是Excel数据对应的实体类,DemoDataListener这看名字就是监听器,用来监听处理读取到的每一条数据

1.2.2. 源码解析

1.2.2.1. 核心源码XlsxSaxAnalyser
  • 它核心的Excel解析我认为是这个类XlsxSaxAnalyser,在它的构造方法中做了很多事
    public XlsxSaxAnalyser(AnalysisContext analysisContext, InputStream decryptedStream) throws Exception {
        ...
        //从这开始将数据读取成inputStream流,缓存到了sheetMap
        XSSFReader xssfReader = new XSSFReader(pkg);
        analysisUse1904WindowDate(xssfReader, readWorkbookHolder);

        stylesTable = xssfReader.getStylesTable();
        sheetList = new ArrayList<ReadSheet>();
        sheetMap = new HashMap<Integer, InputStream>();
        XSSFReader.SheetIterator ite = (XSSFReader.SheetIterator)xssfReader.getSheetsData();
        int index = 0;
        if (!ite.hasNext()) {
            throw new ExcelAnalysisException("Can not find any sheet!");
        }
        while (ite.hasNext()) {
            InputStream inputStream = ite.next();
            sheetList.add(new ReadSheet(index, ite.getSheetName()));
            sheetMap.put(index, inputStream);
            index++;
        }
    }
1.2.2.2. doRead
  • 例子中真正开始做解析任务的是doRead方法,不断进入此方法,会看到真正执行的最后方法就是XlsxSaxAnalyser类的execute方法;可以看到如下方法中parseXmlSource解析的就是sheetMap缓存的真正数据
    @Override
    public void execute(List<ReadSheet> readSheetList, Boolean readAll) {
        for (ReadSheet readSheet : sheetList) {
            readSheet = SheetUtils.match(readSheet, readSheetList, readAll,
                analysisContext.readWorkbookHolder().getGlobalConfiguration());
            if (readSheet != null) {
                analysisContext.currentSheet(readSheet);
                parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(analysisContext, stylesTable));
                // The last sheet is read
                analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext);
            }
        }
    }
1.2.2.3. 概述DemoDataListener实现
  • 对应我们用户需要手写的代码,我们的监听器DemoDataListener中有两个实现方法如下,invoke就对应了上述代码中的parseXmlSourcedoAfterAllAnalysed对应了上述方法中的notifyAfterAllAnalysed,分别表示了先解析每一条数据和当最后一页读取完毕通知所有监听器
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
1.2.2.4. parseXmlSource具体实现
  • 看标识重点的地方,这是最核心的解析地
    private void parseXmlSource(InputStream inputStream, ContentHandler handler) {
        InputSource inputSource = new InputSource(inputStream);
        try {
            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            SAXParser saxParser = saxFactory.newSAXParser();
            XMLReader xmlReader = saxParser.getXMLReader();
            xmlReader.setContentHandler(handler);
            //重点
            xmlReader.parse(inputSource);
            inputStream.close();
        } catch (ExcelAnalysisException e) {
            throw e;
        } catch (Exception e) {
            throw new ExcelAnalysisException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new ExcelAnalysisException("Can not close 'inputStream'!");
                }
            }
        }
    }
  • 由于这层层深入非常多,我用一张截图来表现它的调用形式

751560-20191024173839114-883954871.png

1.2.2.5. notifyAfterAllAnalysed具体实现
  • 具体看notifyAfterAllAnalysed的代码,我们实现的DemoDataListener监听器继承AnalysisEventListener,而AnalysisEventListener实现ReadListener接口
    @Override
    public void notifyAfterAllAnalysed(AnalysisContext analysisContext) {
        for (ReadListener readListener : readListenerList) {
            readListener.doAfterAllAnalysed(analysisContext);
        }
    }

1.3. Excel写

1.3.1. 例子

  • 如下例子,使用还是简单的,和读比较类似
    /**
     * 最简单的写
     * <p>1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
     * <p>2. 直接写即可
     */
    @Test
    public void simpleWrite() {
        String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }
    
    private List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

1.3.2. 源码解析

1.3.2.1. doWrite
  • 和读一样doWrite才是实际做事的,这次我们从这个入口跟进
    public void doWrite(List data) {
        if (excelWriter == null) {
            throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method");
        }
        excelWriter.write(data, build());
        excelWriter.finish();
    }
1.3.2.2. write
  • 很明显,write是核心,继续进入ExcelWriter类,看名字addContent就是添加数据了,由excelBuilderExcel建造者来添加,这是ExcelBuilderImpl
    public ExcelWriter write(List data, WriteSheet writeSheet, WriteTable writeTable) {
        excelBuilder.addContent(data, writeSheet, writeTable);
        return this;
    }
1.3.2.3. addContent
  • 可以看到如下,显示封装和实例化一些数据,创建了ExcelWriteAddExecutor写数据执行器,核心就是add方法了
    @Override
    public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable) {
        try {
            if (data &#
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,根据您的问题,我假设您要使用EasyExcel解析Excel表格,并将解析结果添加到每一行的最后一列。可以按照以下步骤操作: 1. 定义要添加的解析结果 假设您要将解析结果添加到每一行的最后一列之后,可以定义一个List,存放解析结果: ``` List<String> result = new ArrayList<>(); result.add("解析结果1"); result.add("解析结果2"); result.add("解析结果3"); // ... ``` 2. 读取Excel表格 使用EasyExcel读取Excel表格,得到Excel表格的数据: ``` // 定义Excel读取监听器 ReadListener listener = new ReadListener() { // 实现onRead方法,在读取到每一行数据时调用 @Override public void onRead(List<Object> data, AnalysisContext context) { // 处理读取到的数据,并将解析结果添加到每一行的最后一列之后 int lastIndex = data.size() - 1; data.add(lastIndex + 1, result.get(context.readRowHolder().getRowIndex())); } }; // 使用EasyExcel读取Excel表格 ExcelReader reader = EasyExcel.read("input.xlsx", listener).build(); reader.read(); ``` 3. 写出更新后的数据 使用EasyExcelwrite方法,将更新后的数据写出到指定的目标中,例如写出到本地文件: ``` // 创建ExcelWriter对象 ExcelWriter writer = EasyExcel.write("output.xlsx").build(); // 写出数据到Excel文件中 writer.write(data, sheet); // 关闭ExcelWriter对象 writer.finish(); ``` 其中,data表示更新后的数据,sheet表示要更新的sheet
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值