加载大Zip文件内存溢出问题(Asp.Net C#控制器解决方案)
解决Asp.Net C#控制器大Zip文件下载内存溢出问题
问题背景
在Asp.Net C#控制器中使用MemoryStream处理Zip文件时,当文件超过500MB(当前为800MB)出现OutOfMemory内存溢出错误,需要修改代码实现大Zip文件的流式下载。
原代码核心问题:
- 用
MemoryStream将整个Zip文件加载到内存,大文件直接耗尽内存 - 循环内重复创建
HttpClient实例,造成资源浪费 - 下载文件先写入临时磁盘文件再加入Zip,多了不必要的IO步骤且未清理临时文件
优化方案
直接通过Response.Body流式输出Zip内容,避免内存缓存整个文件,同时优化资源使用:
关键改进点
- 抛弃
MemoryStream,直接将Zip写入HttpContext.Response.Body - 复用
HttpClient实例(推荐用IHttpClientFactory注入,避免循环内反复创建) - 下载文件时直接流式写入Zip条目,跳过临时文件
- 正确设置响应头,确保浏览器识别为Zip下载
优化后的控制器代码
[HttpGet(ZipFileMinhChung)] [ProducesResponseType(typeof(VoidMethodResult), (int)HttpStatusCode.BadRequest)] public async Task<IActionResult> ZipFileMinhChungAsync([FromQuery] BaoCaoDanhMucMinhChungRequestViewModel request) { var result = await _reportQueries.ExportZipFileMinhChungAsync(request).ConfigureAwait(false); if (!result.Any()) { return BadRequest(); } var zipName = $"FilesMinhChung-{DateTime.Now:yyyy_MM_dd_HHmmss}.zip"; // 设置响应头,告知浏览器下载文件 Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{zipName}\""); Response.ContentType = "application/zip"; // 复用HttpClient(建议通过IHttpClientFactory注入,此处为简化示例) using var httpClient = new HttpClient(); httpClient.BaseAddress = new Uri(_configuration["Gateway"]); // 直接向Response.Body写入Zip内容,无需内存缓存 using var archive = new ZipArchive(Response.Body, ZipArchiveMode.Create, leaveOpen: true); foreach (var item in result) { var entryPath = $"TieuChi{item.MaTieuChuanTieuChi}/{item.MaMinhChung}/{item.FileName}"; var zipEntry = archive.CreateEntry(entryPath, CompressionLevel.Optimal); // 流式下载文件并直接写入Zip条目,跳过临时文件 using var entryStream = zipEntry.Open(); using var fileStream = await httpClient.GetStreamAsync(item.FilePath); await fileStream.CopyToAsync(entryStream); } return new EmptyResult(); }
额外注意事项
- 若使用.NET Core/.NET 5+,
Response.Body默认支持异步写入,无需额外配置 - 优先通过依赖注入使用
IHttpClientFactory管理HttpClient,避免手动创建带来的资源泄漏风险 - 可根据需求调整
CompressionLevel,平衡压缩速度与生成文件大小 - 建议添加异常捕获逻辑,避免单个文件下载失败导致整个Zip生成中断
内容的提问来源于stack exchange,提问作者Hung Anh Nguyen
相关产品推荐
相关产品推荐

