生成大型Excel电子表格时出现了OutOfMemoryException异常。

3
我正在一个ASP.NET应用程序中使用NPOI 1.2.3.0将一次相当大的SQL查询结果导出到Excel 2003 XLS文件。简单来说,查询结果被填充到一个ADO.NET DataTable中。然后我有一个循环遍历DataTable中的行,并为每一行添加一个NPOI电子表格行的例程。它足够智能,一旦单个工作表超过65,000行,就会创建一个新工作表,并在那里继续行,从新工作表的第一行开始。 这种方法对于我的一些较小的数据库查询非常有效,例如包括30,000行和50列的查询,但我有一个查询返回超过125,000行并且大约有50个列,其中许多具有大量文本。 我能够构建电子表格没有问题,但是当我尝试将生成的电子表格流式传输到浏览器时,当调用HSSFWorkbook类的Write方法时,我会收到OutOfMemoryException。(在内部,当Write方法调用该类的GetBytes方法时,错误会发生。)
如果我运行调试器并在调用Write方法之前停止,我会发现工作簿的Size属性返回大约6500万的值。
这个错误在CodePlex的NPOI项目中被记录下来 - 请参见标题为Out of Memory Problems的讨论 - 不幸的是没有找到解决方案。
为了完整起见,这里是引发异常的代码(具体来说,它在workbook.Write行上引发)。
Using exportData As New MemoryStream()
    workbook.Write(exportData)

    Response.ContentType = "application/vnd.ms-excel"
    Response.AddHeader("Content-Disposition", "Attachment;Filename=" & saveAsName)
    Response.Clear()
    Response.BinaryWrite(exportData.GetBuffer())
    Response.End()
End Using

谢谢!


1
嗨,斯科特 - 我记得读到内存流对象在容量方面的限制,我认为在32位环境中是512MB。你尝试过将Excel文档写入不同类型的流吗? - Dave Long
如果这确实是MemoryStream本身的限制,您可以使用Win32 API的包装器来避免磁盘IO(如果需要):例如:https://github.com/tomasr/filemap - Dave Long
@Dave,使用FileStream将其写入磁盘不会导致任何错误,并成功生成电子表格。听起来MemoryStream可能是罪魁祸首。生产环境是64位的,所以我不知道FileMap类是否是正确的选择。 - Scott Mitchell
嗨,Scott - 好的,在64位环境中,Memory Stream对象将处理高达2GB的内存数据,所以你可能没问题,此外,我认为Framework 4提供了内置的包装类 - 我还没有研究过这些,但值得一看 - 如果4.0是一个选项。 - Dave Long
@Dave,很遗憾目前不支持4.0选项。好消息是我们的暂存环境是64位的,所以我们可以在那里进行测试。我已经验证了我的代码可以处理较小的Excel文件,并且非常有信心它可以工作,因此希望我们可以在暂存环境中进行测试以澄清该问题是否存在。再次感谢您的帮助! - Scott Mitchell
没问题 - 希望它能成功 :-) - Dave Long
2个回答

1

在这种情况下,考虑到FileStream对象不会引起错误,而错误是由32位的512MB容量限制和64位的2GB限制引起的,我会尝试将文件写入MemoryStream,并捕获错误,如果遇到错误,则返回到FileStream来处理更大的文件。

这里显然存在性能权衡,但是如果你的用户下载超过2GB的文件,他们应该预计速度会变慢一些 :-)

如果这对你有用,我很感兴趣知道。

谢谢, Dave


有一件事让我感到困惑,那就是生成的Excel文件大小约为85 MB。我不清楚一个大小为85 MB的Excel文档如何会超过512MB的限制。 - Scott Mitchell
嗨,Scott - 这是一个困难的问题,说实话我也不确定。如果我必须猜测,我会说它与HSSPWorkbook将数据缓冲到内存流的方式有关,可能存在某种填充,但是如果不知道具体实现的细节,就不清楚了。这也可能与COMInterop有关,但是在这里我只是猜测...等我有时间仔细研究一下,我会确保告诉你我发现了什么。 - Dave Long

0

NPOI不仅使用MemoryStream,还使用字节数组。主要原因是字节数组。但是NPOI目前必须使用字节数组。暂时没有计划更改这一点。对造成的任何不便,我们深表歉意。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接