为什么 POI 需要花费这么多内存来处理 xlsx

问题描述 投票:0回答:1

我有一个xlsx文件,大小为90MB,不是很大。

首先我使用 XSSFWorkbook 来读取它,然后出现 OutOfMemory 错误。嗯,我改用XSSF和SAX(Event API)来读取。

当我尝试编写 xlsx 文件时,文档 https://poi.apache.org/components/spreadsheet/how-to.html#sxssf 告诉 “SXSSF 刷新临时文件中的工作表数据(每张工作表一个临时文件),这些临时文件的大小可能会增长到非常大的值。例如,对于 20 MB 的 csv 数据,临时 xml 的大小会超过 1 GB .”

为什么要花费这么多存储空间?

apache-poi
1个回答
2
投票

2007 及更高版本的所有 Microsoft Office 文件都是 Office Open XML 文件。这是在特殊目录结构中包含 XML 文件和其他嵌入文件的 ZIP 存档。

内存消耗由使用的 XML 格式决定。

举个例子:

CSV:

Name, Class, Amount
Name1, Class1, 1234.56

长度:43 个字符。

最小工作表-XML:

<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
 <dimension ref="A1"/>
 <sheetViews>
  <sheetView workbookViewId="0" tabSelected="true"/>
 </sheetViews>
 <sheetFormatPr defaultRowHeight="15.0"/>
 <sheetData>
  <row r="1">
   <c r="A1" t="inlineStr">
    <is>
     <t>Name</t>
    </is>
   </c>
   <c r="B1" t="inlineStr">
    <is>
     <t>Class</t>
    </is>
   </c>
   <c r="c1" t="inlineStr">
    <is>
     <t>Amount</t>
    </is>
   </c>
  </row>
  <row r="2">
   <c r="A2" t="inlineStr">
    <is>
     <t>Name1</t>
    </is>
   </c>
   <c r="B2" t="inlineStr">
    <is>
     <t>Class1</t>
    </is>
   </c>
   <c r="B2" t="n">
    <v>1234.56</v>
   </c>
  </row>
 </sheetData>
 <pageMargins bottom="0.75" footer="0.3" header="0.3" left="0.7" right="0.7" top="0.75"/>
</worksheet>

长度:854 个字符。

因此使用的文本字节数量增加了 20 倍。这只是一个非常简单的例子。

每个单元格值的最小开销为 56 个字符:

 <c r=".." t="inlineStr">
  <is>
   <t></t>
  </is>
 </c>

与 CSV 数据相比。

生成的

*.xlsx
文件是一个 ZIP 文件,正在使用压缩。但默认情况下,临时工作表文件不会被压缩,以节省时间和随机存取存储器。但是您链接的资源充分说明:

SXSSF 刷新临时文件中的工作表数据(每张工作表一个临时文件) 这些临时文件的大小可能会增长到非常大的值。 例如,对于 20 MB 的 csv 数据,临时 xml 的大小变为 超过一千兆字节。 如果临时文件的大小是一个问题,您 可以告诉SXSSF使用gzip压缩:

SXSSFWorkbook wb = new SXSSFWorkbook();
wb.setCompressTempFiles(true); // temp files will be gzipped

因此,当临时文件的大小成为问题时,它提供了一个解决方案。当然,这会花费时间和随机存取存储器。

但是,正如已经说过的,使用 XML 作为文件格式有其自身的成本。

当谈到为什么 Excel 使用 XML 来实现

*.xlsx
时,答案很复杂,并且可能基于观点。

与纯文本 CSV 相比有一些优点。例如,不同的数据类型可以存储得更加节省。可以存储数据格式。还可以存储单元格样式和格式...除了 CSV 永远无法存储的所有内容:绘图、图片、图表、单元格注释...

但是对于

*.xls
使用的以前的二进制 BIFF 格式来说也是如此。而且这样的内存使用量要少得多,因为二进制格式更接近于二进制计算。

但是所有二进制 Office 格式都是闭源的,微软面临着发布文件格式定义的压力。 XML 是当时 Office 文件最常用的格式。例如,已经有 OpenOffice。所以...

OpenOffice 使用 OpenDocument 文件格式。这也是在特殊目录结构中包含 XML 文件和其他嵌入文件的 ZIP 存档。但内部结构和使用的 XML 模式与 Microsoft 的不同

Office Open XML

上面是关于XML的内存开销。但为什么 Apache POI 在处理 Office Open XML 格式时需要更多内存?

Microsoft Office 在打开文件时将 XML 解析为随机存取存储器中闭源专有二进制格式的对象。 OpenOffice/Libreoffice 的做法类似,但使用开源代码。然后它仅使用该二进制格式进行操作。 XML 仅在打开时解析,仅在保存文件时生成。就内存使用而言,这比直接操作 XML 对象更有效。

Apache POI 使用类的 Java 对象,这些对象是使用 XMLBeans 从 XML 模式创建的。这意味着在后台内部解析 XML 时,每个 XML 元素都会在内存中获得它自己的 Java 类和它自己的 Java 对象。这会在纯 XML 文本开销之外产生额外的内存开销。所有更改都是使用这些对象进行的。因此,所有这些对象都需要保留在内存中,直到 XML 保存到文件中。

© www.soinside.com 2019 - 2024. All rights reserved.