Asp.net 生成大型Excel电子表格时OutOfMemoryException

Asp.net 生成大型Excel电子表格时OutOfMemoryException,asp.net,out-of-memory,npoi,Asp.net,Out Of Memory,Npoi,我正在ASP.NET应用程序中使用NPOI 1.2.3.0将相当大的SQL查询结果导出到Excel 2003 XLS文件 简而言之,查询结果填充到ADO.NET数据表中。然后我有一个例程,它循环遍历DataTable中的行,并为每一行向NPOI电子表格添加一行。如果一张工作表超过65000行,则会创建一张新工作表,并从新工作表的第一行开始在该工作表中继续行 这种方法适用于我的一些较小的数据库查询,例如30000行和50列,但我有一个查询返回125000行以北,大约有50列,其中许多包含大量文本

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

简而言之,查询结果填充到ADO.NET数据表中。然后我有一个例程,它循环遍历DataTable中的行,并为每一行向NPOI电子表格添加一行。如果一张工作表超过65000行,则会创建一张新工作表,并从新工作表的第一行开始在该工作表中继续行

这种方法适用于我的一些较小的数据库查询,例如30000行和50列,但我有一个查询返回125000行以北,大约有50列,其中许多包含大量文本

我可以毫无问题地构建电子表格,但是当我尝试将生成的电子表格流式传输到浏览器时,调用
HSSFWorkbook
类的
Write
方法时,会得到一个
OutOfMemoryException
。(在内部,Write方法调用类的
GetBytes
方法时会发生错误。)

如果在调用Write方法之前运行调试器并停止,我会看到工作簿的Size属性返回(大约)6500万的值

这个错误在CodePlex的NPOI项目中被注意到——请参阅标题为的讨论——但不幸的是,没有找到解决方案

为了完整起见,下面是引发异常的代码(具体来说,它是在
工作簿.Write
行中引发的)


谢谢

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

这里有一个明显的性能权衡,但如果用户下载的文件大于2GB,他们可能会认为下载速度会慢一点:-)

我很想知道这是否适合你

谢谢,
Dave不仅使用MemoryStream,还使用字节数组。主要的根本原因是字节数组。但到目前为止,我不得不使用字节数组。目前还没有改变这一状况的计划。很抱歉给您带来不便。

Hi Scott-我记得读过关于内存流对象在容量方面的限制,我认为在32位环境中是512MB。您是否尝试过将Excel文档写入不同类型的流?如果这确实是memorystream本身的一个限制,您可以使用Win32 api的包装器来避免磁盘IO(如果需要):例如:@Dave,使用文件流将其写入磁盘不会导致任何错误并成功生成电子表格。听起来记忆流可能是罪魁祸首。生产环境是64位的,所以我不知道FileMap类是否适合。嗨,Scott-好的,在64位环境中,memory Stream对象将在内存中处理高达2GB的数据,所以您可能还可以,另外,我认为Framework 4提供了内置包装类-我没有研究这些,但是,如果4.0是一个新版本的话,也许值得一看option@Dave遗憾的是,4.0现在不是一个选项。好消息是我们的暂存环境是64位的,所以我们可以在那里进行测试。我已经用较小的Excel文件验证了我的代码的有效性,并且我很有信心它可以工作,所以希望这是我们可以在登台测试中测试的东西,以澄清问题是否会出现在那里。再次感谢你的帮助!让我困惑的是,生成的Excel文件大小约为85MB。我不清楚这样大小的Excel文档怎么会超过512MB的限制。嗨,斯科特,这是一个困难的问题,老实说,我也不确定。如果我不得不猜测的话,我会说这与HSSPWorkbook将数据缓冲到memorystream中的方式有关,可能存在某种填充,但在不知道实现细节的情况下,这是不清楚的。这也可能与共产国际有关,但我只是在这里猜测。。。当我有时间的时候,我会更详细地调查一下,一定会让你知道我发现了什么。
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