我有一个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 .”
为什么要花费这么多存储空间?
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 保存到文件中。