Java poi 4.0+ (三) 事件驱动模式解析excel 包含03和07版的excel

本文介绍了使用Java POI的事件驱动模式处理大数据量的Excel文件,以解决用户模式在处理大量数据时内存消耗过大的问题。通过提供07版和03版Excel的解析示例,详细解释了如何实现流式读取,从而有效降低内存占用。此外,还提到了中断解析的方法以及推荐使用异步线程池来进一步提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面我们介绍了用户模式如何解析excel,但是用户模式局限很大,其并不能解析大数据量的excel。我做过测试,在16G内存上的机子,07版excel导入6W条数据,内存一下彪了2个G,由于之前忘截图了,所以此处没有图,下面,我将介绍在大批量数据时候,如何解析excel。

当然是用 事件驱动模式(其实就是一种流式读取的名字)了!

网上有很多相似的代码,我也参考了很多,但是我发现不论哪个都有很大的问题,比如对公式和数字日期的解析。

下面不多说,直接上代码。参考了网上很多的代码,也给自己记录一下。

 

首先07版的,要继承  DefaultHandler 把他当做xml解析。这个网上有很多说明,我就不赘述了。xlsx改成rar后解压得到如下图。

 

 

package com.youth.excel_youth.util;

import com.youth.excel_youth.constants.Constants;
import com.youth.excel_youth.constants.XSSFDataType;
import com.youth.excel_youth.service.IRowReader;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: youth_1231
 * @Date: 2019/1/24 0024 15:39
 * @Description:   抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析
*                    xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低
 *                   内存的耗费,特别使用于大数据量的文件。
 */
public class Excel07Parser extends DefaultHandler {
    //共享字符串表
    private SharedStringsTable sst;
    //上一次的内容
    private String readValue;
    /**
     * 存放一行中的数据
     */
    private String[] rowList;

    private int colIdx;
    private int sheetIndex = -1;

    //当前行
    private int curRow = 0;
    /**
     * T元素标识
     */
    private boolean isTElement;
    /**
     * 单元格类型
     */
    private XSSFDataType dataType;

    private StylesTable stylesTable;
    private short dataFormat;
    private String dataFormatString;

    private IRowReader rowReader;
    /**
     * 可以将有的sheet页相关参数放到一个对象中,比如sheet中的错误,sheetName,业务数据等等,
     * 对象放到集合中,其中的数据建议超过2000条持久化一次
     */
//    private List<XXX> xxxxx;


    public void setRowReader(IRowReader rowReader){
        this.rowReader = rowReader;
    }

    /**
     * 遍历工作簿中所有的电子表格
     * @param filename
     * @throws Exception
     */
    public void process(String filename) throws Exception {
        OPCPackage pkg = OPCPackage.open(filename);
        XSSFReader r = new XSSFReader(pkg);
        this.stylesTable = r.getStylesTable();
        SharedStringsTable sst = r.getSharedStringsTable();
        XMLReader parser = fetchSheetParser(sst);
        XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) r.getSheetsData();
        while (sheets.hasNext()) {
            curRow = 0;
            sheetIndex++;
            InputStream sheet = sheets.next();
            /*获取当前sheet名称,有些同学需要*/
            String sheetName = sheets.getSheetName();
            InputSource sheetSource = new InputSource(sheet);
            parser.parse(sheetSource);
            sheet.close();
        }
    }

    public XMLReader fetchSheetParser(SharedStringsTable sst)
            throws SAXException {
        XMLReader parser = XMLReaderFactory
                .createXMLReader();
        this.sst = sst;
        parser.setContentHandler(this);
        return parser;
    }

    @Override
    public void startElement(String uri, String localName, String name,
                             Attributes attributes) throws SAXException {

        // c => 单元格
        if ("c".equals(name)) {
            colIdx = getColumn(attributes);
            dataFormat = -1;
            dataFormatString = null;

            // 如果下一个元素是 SST 的索引,则将nextIsString标记为true
            String cellType = attributes.getValue("t");
            String cellStyle = attributes.getValue("s");

            this.dataType = XSSFDataType.NUMBER;
            if ("b".equals(cellType)) {
                this.dataType = XSSFDataType.BOOLEAN;
            } else if ("e".equals(cellStyle)) {
                this.dataType = XSSFDataType.ERROR;
            } else if ("s".equals(cellStyle)) {
                this.dataType = XSSFDataType.SSTINDEX;
            } else if ("inlineStr".equals(cellStyle)) {
                this.dataType = XSSFDataType.INLINESTR;
            } else if ("str".equals(cellStyle)) {
                this.dataType = XSSFDataType.FORMULA;
            }

            if (cellStyle != null) {
                int styleIndex = Integer.parseInt(cellStyle);
                XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
                dataFormat = style.getDataFormat();
                dataFormatString = style.getDataFormatString();
                /**
                 * 07版本当前只发现了57 58 的时候formatString为空
                 */
                if (!Constants.EXCEL_FORMAT_INDEX_DATA_EXACT_NY.equals(dataFormat) && !Constants.EXCEL_FORMAT_INDEX_DATA_EXACT_YR.equals(dataFormat)
                        && !Constants.EXCEL_FORMAT_INDEX_TIME_EXACT.contains(dataFormat)
                        && dataFormatString == null){
                    this.dataType = XSSFDataType.NULL;
                    dataFormatString = BuiltinFormats.getBuiltinFormat(dataFormat);
                }
            }

        }
        //当元素为t时
        if("t".equals(name)){
            isTElement = true;
        } else {
            isTElement = false;
        }

        // 解析到一行开始处,初始化数据
        if("row".equals(name)){
            rowList = new String[getColsNum(attributes)];
        }
        // 置空
        readValue = "";
    }

    @Override
    public void endElement(String uri, String localName, String name)
            throws SAXException {


        if (isTElement) {
            rowList[colIdx] = readValue.trim();
            isTElement = false;
        } else if ("v".equals(name)){
            getValue();
            rowList[colIdx] = readValue;
        } else {
            //如果标签名为 row,这说明已经到行尾,调用getRows()方法
            if ("row".equals(name)){
                rowReader.getRows(sheetIndex,curRow,new ArrayList<>(Arrays.asList(rowList)));
                curRow++;
            }
        }

    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        //得到单元格内容的值
        readValue += new String(ch, start, length);
    }

    /**
     * 事件模式: 得到当前cell在当前row的位置
     * @param attributes
     * @return
     */
    private int getColumn(Attributes attributes){
        String name = attributes.getValue("r");
        int column = -1;
        for (int i = 0; i < name.length(); ++i) {
            if (Character.isDigit(name.charAt(i))){
                break;
            }

            int c = name.charAt(i);
            column = (column + 1) * 26 + c - 'A';
        }
        return column;
    }

    /**
     * 事件模式: 得到当前cell在当前row的位置
     * @param attributes
     * @return
     */
    private int getColsNum(Attributes attributes){
        String s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值