gpt4 book ai didi

c# - 使用返回 OutOfMemoryException 的 Web API 下载大文件

转载 作者:行者123 更新时间:2023-11-30 21:37:03 25 4
gpt4 key购买 nike

我已尝试在 SO 上使用大量问题/答案 - 但似乎无法克服我在尝试通过 Web API 下载 200MB zip 文件时收到的 OutOfMemoryException

为了测试,我已经大大简化了我的代码:

    [HttpPost]
public async Task<HttpResponseMessage> ExportReports(OrderExportFilter filterJson)
{

var filename = "C:\\pdftemp\\1128d0ff-a4b7-440d-9e3b-dd152445eb62.zip";

var fileStream = File.OpenRead(filename);

var content = new StreamContent(fileStream, 4096);

resp.Content = content;

resp.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
resp.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "OrdersExport.pdf"
};

return resp;
}

这只是我尝试下载 zip 文件的多种方式之一,我还 referenced this link并复制代码无济于事。

我不知道我做错了什么才能下载大型 zip 文件。

更新:根据评论中的请求,我尝试使用“application/octet-stream”代替 - 仍然没有运气 - 同样的错误。

另外 - 还需要注意一件事 - 当我使用此代码下载较小的 zip 文件时它工作正常,似乎一旦文件太大就会开始爆炸。

异常更新:

我能够提取异常详细信息:

    {
"Message": "An error has occurred.",
"ExceptionMessage": "Exception of type 'System.OutOfMemoryException' was thrown.",
"ExceptionType": "System.OutOfMemoryException",
"StackTrace": " at System.IO.MemoryStream.set_Capacity(Int32 value)\r\n at System.IO.MemoryStream.EnsureCapacity(Int32 value)\r\n at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)\r\n at System.IO.Compression.DeflateStream.WriteDeflaterOutput(Boolean isAsync)\r\n at System.IO.Compression.DeflateStream.PurgeBuffers(Boolean disposing)\r\n at System.IO.Compression.DeflateStream.Dispose(Boolean disposing)\r\n at System.IO.Stream.Close()\r\n at System.IO.Compression.GZipStream.Dispose(Boolean disposing)\r\n at System.IO.Stream.Close()\r\n at System.IO.Stream.Dispose()\r\n at System.Net.Http.Extensions.Compression.Core.Compressors.BaseCompressor.<Compress>d__4.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Net.Http.Extensions.Compression.Core.Models.CompressedContent.<SerializeToStreamAsync>d__4.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()"
}

最佳答案

您的堆栈跟踪表明当 DeflateStream 关闭时抛出异常。此流显然写入 MemoryStream 并关闭 DeflateStream 将整个(压缩的)响应输出到 MemoryStream。也就是说,分配了 MemoryStream 中的字节数组缓冲区,如果压缩输出太大,它会失败并返回 OutOfMemoryException

令人惊讶的是,压缩后的响应被缓存在内存中,因为网络服务器至少在概念上能够通过压缩器将文件中的数据流式传输到网络,而无需先读取整个文件并将其压缩到内存中。

根据评论,我猜测您正在 Web API 应用程序中使用 NuGet 包 Microsoft.AspNet.WebApi.Extensions.Compression.Server。虽然我不能声称我完全理解它是如何工作的,但我确实尝试反编译它,并且从我可以看到的结果来看,通过这个中间件发送响应导致输出被缓冲而不是一次,实际上是两次。 (BaseCompressor.Compress() 方法写入由 StreamManager.GetStream() 创建的 MemoryStream 和此 MemoryStream然后被读入一个字节数组,有效地加倍了压缩器的内存需求。)

根据此分析,您应该关闭大响应的压缩,这似乎已通过将属性 [Compression(Enabled = false)] 应用于您的 Controller 操作来解决您的问题。

具有讽刺意味的是,您无法压缩可能从压缩中获益最多的大响应。但是,如果您在像 IIS 这样 native 支持响应压缩的 Web 服务器上运行 Web API,您可以完全删除中间件,而是在 Web 服务器中打开压缩(在 IIS 中称为 dynamic compression)。 native 压缩的性能也可能优于托管代码中的压缩。

关于c# - 使用返回 OutOfMemoryException 的 Web API 下载大文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47661996/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com