如何使用Apache POI加载大型xlsx文件?

2022-08-31 17:49:20

我有一个大.xlsx文件(141 MB,包含293413行,每行62列),我需要在其中执行一些操作。

我在加载此文件 () 时遇到问题,因为 POI 在 XSSF (xlsx) 工作簿上占用大量内存。OutOfMemoryError

此 SO 问题与此类似,提供的解决方案是增加 VM 的已分配/最大内存。

它似乎适用于这种文件大小(9MB),但对我来说,即使分配所有可用的系统内存,它也根本不起作用。(好吧,考虑到文件大15倍以上也就不足为奇了)

我想知道是否有任何方法可以加载工作簿,它不会消耗所有内存,但是,无需执行基于(进入)XSSF的基础XML的处理。(换句话说,保持清教徒POI解决方案)

如果没有困难,欢迎您说出来(“没有”),并向我指出“XML”解决方案的方法。


答案 1

我在Web服务器环境中也处于类似的情况。上传的典型大小约为 150k 行,从单个请求中消耗大量内存是不好的。Apache POI Streaming API对此非常有效,但它需要完全重新设计您的读取逻辑。我已经有一堆使用标准API的读取逻辑,我不想重做,所以我写了这个:https://github.com/monitorjbl/excel-streaming-reader

它并不完全是标准类的直接替代品,但如果您只是循环访问行,它的行为类似:XSSFWorkbook

import com.monitorjbl.xlsx.StreamingReader;

InputStream is = new FileInputStream(new File("/path/to/workbook.xlsx"));
StreamingReader reader = StreamingReader.builder()
        .rowCacheSize(100)    // number of rows to keep in memory (defaults to 10)
        .bufferSize(4096)     // buffer size to use when reading InputStream to file (defaults to 1024)
        .sheetIndex(0)        // index of sheet to use (defaults to 0)
        .read(is);            // InputStream or File for XLSX file (required)

for (Row r : reader) {
  for (Cell c : r) {
    System.out.println(c.getStringCellValue());
  }
}     

使用它有一些警告;由于XLSX工作表的结构方式,并非所有数据在流的当前窗口中都可用。但是,如果您只是尝试从单元格中读取简单数据,则效果很好。


答案 2

通过使用文件而不是流,可以提高内存使用率。(最好使用流式处理 API,但流式处理 API 有限制,请参阅 http://poi.apache.org/spreadsheet/index.html)

所以不是

Workbook workbook = WorkbookFactory.create(inputStream);

Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));

这是根据: http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream

文件与输入流

“打开工作簿时,无论是.xls HSSFWorkbook,还是.xlsx XSSFWorkbook,都可以从文件或 InputStream 加载工作簿。使用File对象可以降低内存消耗,而InputStream需要更多的内存,因为它必须缓冲整个文件。


推荐