在处理大数据量的 Excel 时,避免内存溢出的一种解决方案是使用 SAX 方式解析 Excel 文件,而不是使用 POI 的 DOM 方式。

SAX 是一种事件驱动的解析方式,它逐行读取 Excel 文件,而不是将整个文件加载到内存中。这样可以大大减少内存的使用量。

以下是使用 SAX 方式解析 Excel 文件的示例代码:

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
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.util.ArrayList;
import java.util.List;

public class ExcelParser {
    private static final int BATCH_SIZE = 1000; // 批量处理的行数

    public static void parseExcel(InputStream inputStream) throws Exception {
        OPCPackage opcPackage = OPCPackage.open(inputStream);
        XSSFReader xssfReader = new XSSFReader(opcPackage);
        SharedStringsTable sharedStringsTable = xssfReader.getSharedStringsTable();
        
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        ContentHandler contentHandler = new ExcelSheetHandler(sharedStringsTable);
        xmlReader.setContentHandler(contentHandler);

        SheetIterator sheetIterator = (SheetIterator) xssfReader.getSheetsData();
        while (sheetIterator.hasNext()) {
            InputStream sheetInputStream = sheetIterator.next();
            InputSource inputSource = new InputSource(sheetInputStream);
            xmlReader.parse(inputSource);
            sheetInputStream.close();
        }
        
        opcPackage.close();
    }

    private static class ExcelSheetHandler extends DefaultHandler {
        private SharedStringsTable sharedStringsTable;
        private List<String> rowValues;
        private int rowNum;

        public ExcelSheetHandler(SharedStringsTable sharedStringsTable) {
            this.sharedStringsTable = sharedStringsTable;
            this.rowValues = new ArrayList<>();
            this.rowNum = 0;
        }

        @Override
        public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
            if (name.equals('row')) {
                rowValues.clear();
                rowNum++;
            } else if (name.equals('c')) {
                String cellType = attributes.getValue('t');
                if (cellType != null && cellType.equals('s')) {
                    rowValues.add(sharedStringsTable.getItem(Integer.parseInt(attributes.getValue('v'))).getString());
                }
            }
        }

        @Override
        public void endElement(String uri, String localName, String name) throws SAXException {
            if (name.equals('row') && rowNum % BATCH_SIZE == 0) {
                // 处理一批数据
                processBatchData(rowValues);
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            // 处理单元格数据
            rowValues.add(new String(ch, start, length));
        }
    }

    private static void processBatchData(List<String> rowValues) {
        // 在这里处理一批数据,可以将数据写入数据库或进行其他操作
        System.out.println(rowValues);
    }
}

使用上述代码,可以按照自己的需求进行处理一批数据(比如将数据写入数据库)。

需要注意的是,上述代码仅适用于处理 XLSX 格式的 Excel 文件,如果需要处理 XLS 格式的 Excel 文件,可以使用 HSSF 方式进行解析。

另外,如果仍然遇到内存溢出的问题,可以考虑增加 JVM 的内存限制,即通过 -Xmx 参数设置 JVM 的最大堆内存大小。例如,可以使用以下命令运行应用程序:

java -Xmx2g -jar your-application.jar

上述命令将 JVM 的最大堆内存大小限制为 2GB。根据实际情况,可以根据需要适当调整内存大小。

SpringBoot 使用 POI 处理 40 万行 Excel 数据时内存溢出解决方案

原文地址: https://www.cveoy.top/t/topic/qgEI 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录