ASP.NET下载1GB以上大文件触发OutOfMemoryException异常如何解决
问题根因
异常的核心原因是代码实现逻辑错误:File.ReadAllBytes会把目标文件完整加载到服务器进程内存中,1GB大小的文件单次请求就需要占用至少1GB的连续内存。
- 首次下载结束后,占用的内存不会被GC立刻回收,同时会产生大量内存碎片
- 第二次发起下载请求时,ASP.NET进程无法分配出足够的连续内存加载第二个1GB文件,直接抛出内存不足异常
- 小文件内存占用低,不会触发连续内存分配失败,因此不会出现异常
修复方案
核心原则是避免将整个文件一次性加载到内存,改用流式分块传输的方式输出文件,全程服务器内存仅占用KB级别的缓冲区空间,完全不受文件大小限制。
通用实现(适配所有ASP.NET Web Forms版本)
- 清空响应中已有的冗余内容
- 关键配置:必须将
Response.Buffer设为False,否则ASP.NET会把所有要输出的内容先缓存到服务器内存,分块传输就失去了意义 - 设置正确的下载响应头,显式告知客户端文件总大小以支持下载进度显示,注意提取纯文件名,不要把服务器物理路径暴露给客户端
- 用固定大小的缓冲区分块读取文件,每读一块就立刻输出给客户端,输出完成后释放缓冲区,不要在内存中留存文件内容
- 检测客户端连接状态,用户主动取消下载时立刻终止传输,避免浪费服务器资源
对应修复后的VB.NET代码如下:
Dim filePath As String = filename ' 传入的文件物理路径 Dim fileInfo As New FileInfo(filePath) Response.Clear() Response.Buffer = False Response.ContentType = "application/octet-stream" ' 处理文件名,兼容特殊字符、空格,避免文件名乱码 Response.AppendHeader("Content-Disposition", $"attachment; filename=""{Path.GetFileName(filePath)}""") Response.AppendHeader("Content-Length", fileInfo.Length.ToString()) ' 缓冲区大小设为8KB即可,可根据服务器带宽调整到16KB/64KB,不要设置过大 Const bufferSize As Integer = 8192 Dim buffer(bufferSize - 1) As Byte Using fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read) Dim readLength As Integer = fileStream.Read(buffer, 0, bufferSize) While readLength > 0 If Response.IsClientConnected Then Response.OutputStream.Write(buffer, 0, readLength) Response.Flush() readLength = fileStream.Read(buffer, 0, bufferSize) Else readLength = 0 End If End While End Using Response.End()
简化实现(内置方法)
如果不需要自定义传输逻辑,可以直接使用ASP.NET内置的Response.TransmitFile方法,该方法内部已经实现了无缓存流式输出,不会把文件加载到内存,代码更简洁:
Response.Clear() Response.ContentType = "application/octet-stream" Response.AppendHeader("Content-Disposition", $"attachment; filename=""{Path.GetFileName(filename)}""") Response.TransmitFile(filename) Response.End()
额外注意事项
- 下载逻辑执行前后不要向响应输出任何其他内容,包括页面视图状态、调试信息、空白字符,否则会导致下载的文件损坏
- 如果需要支持断点续传,可以在上述逻辑基础上额外处理HTTP
Range请求头,返回对应区间的文件块即可 - IIS部署场景下需要确认请求过滤规则、站点下载大小限制没有拦截大文件请求
内容的提问来源于stack exchange,提问作者Gani_tpt
相关产品推荐
相关产品推荐

