关于java:如何读取大小>40MB的XLSX文件

 2022-02-13 

How to read XLSX file of size >40MB

我正在使用 apache-POI 中的 XSSF 来读取 XLSX 文件。我收到一个错误 java.lang.OutOfMemoryError: Java heap space。后来,使用 -Xmx1024m 为 java 类增加了堆大小,仍然重复相同的错误。

代码:

1
2
3
4
5
String filename ="D:\\\\filename.xlsx";
FileInputStream fis = null;
try {
   fis = new FileInputStream(filename);
   XSSFWorkbook workbook = new XSSFWorkbook(fis);

在上面的代码段中,执行在XSSFWorkbook处停止并抛出指定的错误。
有人可以建议更好的方法来读取大型 XLSX 文件。


POI 允许您以流式方式读取 excel 文件。 API 几乎是 SAX 的package器。确保使用接受字符串的构造函数以正确的方式打开 OPC 包。否则你可能会立即耗尽内存。

1
2
OPCPackage pkg = OPCPackage.open(file.getPath());
XSSFReader reader = new XSSFReader(pkg);

现在,阅读器将允许您获取不同部分的 InputStreams。如果您想自己进行 XML 解析(使用 SAX 或 StAX),您可以使用这些。但这需要对格式非常熟悉。

更简单的选择是使用 XSSFSheetXMLHandler。这是读取第一张纸的示例:

1
2
3
4
5
6
7
StylesTable styles = reader.getStylesTable();
ReadOnlySharedStringsTable sharedStrings = new ReadOnlySharedStringsTable(pkg);
ContentHandler handler = new XSSFSheetXMLHandler(styles, sharedStrings, mySheetContentsHandler, true);

XMLReader parser = XMLReaderFactory.createXMLReader();
parser.setContentHandler(handler);
parser.parse(new InputSource(reader.getSheetsData().next()));

mySheetsContentHandler 应该是您自己的 XSSFSheetXMLHandler.SheetContentsHandler 实现。这个类将被提供行和单元格。

但是请注意,如果您的共享字符串表很大(如果您的大表中没有任何重复的字符串,则会发生这种情况),这可能会适度消耗内存。如果内存仍然是个问题,我建议使用原始 XML 流(也由 XSSFReader 提供)。


为了补充@waxwing 的答案,如果输入 XLS(X) 文件受密码保护,您将需要通过使用解密装饰器package原始文件来获取纯输入流。但首先,您需要将文件作为 POIFSFileSystem.

打开

简而言之:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
String pass ="secret";
File file = new File("data/1.xlsx");
   
try (POIFSFileSystem fs = new POIFSFileSystem(file);
   // wrap in org.apache.poi.poifs.filesystem.DocumentFactoryHelper.getDecryptedStream
   InputStream in = DocumentFactoryHelper.getDecryptedStream(fs, pass);
   OPCPackage pkg = OPCPackage.open(in))
{
    XSSFReader reader = new XSSFReader(pkg);
       
    StylesTable styles = reader.getStylesTable();
    ReadOnlySharedStringsTable sharedStrings = new ReadOnlySharedStringsTable(pkg);
    SheetContentsHandler f = new SheetContentsHandler() {
       // ... your implementation of SheetContentsHandler interface ...
    };
    ContentHandler handler = new XSSFSheetXMLHandler(styles, sharedStrings, f, true);
    XMLReader parser = XMLReaderFactory.createXMLReader();
    parser.setContentHandler(handler);
    parser.parse(new InputSource(reader.getSheetsData().next()));
}