【问题标题】:OutOfMemoryException when generating a large Excel spreadsheet生成大型 Excel 电子表格时出现 OutOfMemoryException
【发布时间】:2012-08-05 07:33:48
【问题描述】:

我在 ASP.NET 应用程序中使用 NPOI 1.2.3.0 将相当大的 SQL 查询的结果导出到 Excel 2003 XLS 文件。

简而言之,查询结果被填充到 ADO.NET 数据表中。然后,我有一个循环遍历 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

谢谢!

【问题讨论】:

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

标签: asp.net out-of-memory npoi


【解决方案1】:

在这种情况下我会做什么,记住 FileStream 对象不会导致错误,并且错误是由 32 位的 512MB 容量限制和 64 位的 2GB 限制引起的,尝试将文件写入memoryStream,捕获错误并在遇到错误时恢复到更大文件的 FileStream。

这里有一个明显的性能折衷,但如果您的用户正在下载大于 2GB 的文件,他们可能会认为这会慢一些 :-)

很想知道这是否适合您。

谢谢, 戴夫

【讨论】:

  • 让我感到困惑的是,生成的 Excel 文件大小约为 85 MB。我不清楚这种大小的 Excel 文档会如何超出 512MB 的限制。
  • 嗨,Scott - 这是一个困难的问题,老实说,我也不确定。如果我不得不猜测,我会说它与 HSSPWorkbook 将数据缓冲到内存流中的方式有​​关,可能会发生某种填充,但在不知道实现细节的情况下尚不清楚。它也可能与 COMInterop 有关,但我只是在这里猜测......当我有时间时,我会更详细地研究它,并且一定会让你知道什么我找到了。
【解决方案2】:

NPOI 不仅使用 MemoryStream,还使用字节数组。主要原因是字节数组。但到目前为止,NPOI 必须使用字节数组。目前还没有改变这一点的计划。给您带来的不便,我们深表歉意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 2018-01-22
    • 2011-05-10
    • 2013-02-07
    相关资源
    最近更新 更多