超大Excel文件高效读取方案

 背景

一般Java会使用ApachePOI作为xlsx的默认读取和写入工具包,因为XSSFWorkbook 和XSSFSheet使用了DOM作为xml的解析技术,所以读取时默认会将整个sheet的内容加载到java JVM内存中,所以在超大数据量Excel的生成和读取都是非常消耗资源的操作,本文的方案使用了JDK1.6引入的StAX 相关的技术,以stream方式读取超大的Excel文件,避免了JVM内存占用开销,提高了处理速度。

相关技术栈

 1.DOM/SAX/StAX相关介绍

  详细技术内容可以看相关文章

  https://cloud.tencent.com/developer/article/2423715

  https://mkyong.com/java/how-to-read-xml-file-in-java-stax-parser/

  2.关于POI的SAX方式解析

   https://howtodoinjava.com/java/library/poi-read-excel-with-sax-parser/

实现思路

 1.需借助poi 的OPCPackage。OPCPackage 是 Apache POI 提供的一个用于处理 Open XML 格式文档的包;

2.使用XSSFReader类,XSSFReader这个类可以很容易地获得OOXML .xlsx文件的各个部分,适用于低内存sax解析或类似的情况;

3.xssfReader.getSheetsData()方法,获取Sheet的流迭代器;

4.使用Stax API,以stream的方式读取sheet对应的XML

接口约定和注意事项

1.调用对应API需定义XLSX文件的元数据信息;

2.接口调用传入Inputstream,而非File或FileInputStream,因为该接口不限于读取本地文件,只需要能支持流方式读取的FileSystem都可以适用;

3.使用StAX时候需要添加相应的约束,以防止XML的XXE注入攻击可能;

4.因无法使用POI的Sheet和Cell,读取出来的数据均为String,相应的CellType和日期处理需要单独进行处理;

5.因为使用迭代器接口返回,OPCPackage和对应的Stream不能使用 try with resource编码,需要在读取完成后手动进行关闭;

具体实现

实现工具类

参见代码 https://github.com/robinhood-jim/JavaFramework/blob/develop/common/src/main/java/com/robin/core/fileaccess/iterator/XlsxFileIterator.java

代码片段

public class XlsxFileIterator extends AbstractFileIterator{
    private XMLInputFactory factory;
    private XMLStreamReader streamReader;
    private OPCPackage opcPackage;
    private Iterator<InputStream> sheetStreams;
    private InputStream readStreams;
    private XSSFReader xssfReader;
    private SharedStrings sharedStrings;
    ......
    @Override
    public void beforeProcess() {
        super.beforeProcess();
        try {
            //防 XXE注入
            factory = XMLInputFactory.newFactory();
            factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
            factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
            opcPackage=OPCPackage.open(instream);
            xssfReader=new XSSFReader(opcPackage);
            sheetStreams=xssfReader.getSheetsData();
            sharedStrings=xssfReader.getSharedStringsTable();
            ......
             }catch (Exception ex){

        }
    }
    private void readNext() throws Exception{
        String nodeName;
        int rowNum;
        String rowNumStr;
        int columnPos=0;
        String value;
        boolean sharedValue=false;
        boolean breakTag=false;
        Object targetValue=null;


        while (streamReader.hasNext() && !breakTag){
          ......
        }
    }
}

说明

1.AbstractFileSystemAccessor 负责文件系统读取输入XLSX文件,返回InputStream,对应的FileSystem包含Local、HDFS、ApacheVFS支持的FileSystem,以及各类云存储(Aliyun、TencentCos等);

2.DataCollectionMeta 通用元数据定义,包含数据项和资源类型以及相关参数;

3.目前版本暂不支持公式字段的计算,需要生成的XLSX文件中公式字段包含结果值;

4.后续支持公式计算,也只能支持本行内操作,跨行因为是迭代器模式无法支持;

5.后续公式因无法调用POI的API,也仅能支持四则运算相关;

调用实例

参见 https://github.com/robinhood-jim/JavaFramework/blob/develop/common/src/test/java/com/robin/comm/test/TestExcelOperation.java

中的testReadWithIterator 测试方法

测试需要的实例数据可以调用该类的testGenerate方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值