C#通过HTTP传输10KB分块字节数组 解决低内存客户端下载内存异常
解决方案
现有代码的问题
- 服务端文件读取逻辑不合理:
File.ReadAllBytes会将整个70KB文件一次性加载到服务端内存,属于不必要的内存开销 - HTTP头配置错误:
ContentType参数错误填写为.bin,不符合HTTP规范,二进制文件的正确MIME类型为application/octet-stream - 输出缓冲未关闭:ASP.NET默认会开启响应输出缓冲,攒够缓冲区大小才会发送数据到客户端,即使代码里做了分块写入,实际客户端还是可能一次性收到全部内容
- 分块传输逻辑缺失:没有明确声明分块传输,部分旧客户端会等待完整响应接收完成才返回给上层业务,还是会触发内存超限问题
修复后的服务端代码
public class FileDownloadHandler : IHttpHandler { public bool IsReusable => true; public void ProcessRequest(HttpContext context) { var fileExtension = GettingExtension(context.Request.RawUrl); if (!fileExtension.Equals(".bin", StringComparison.InvariantCultureIgnoreCase)) { context.Response.Write("Invalid extension"); context.Response.Flush(); return; } string fileName = @"C:\FileHandler\File\AA_GS_BUILD.bin"; const int chunkSize = 10 * 1024; // 固定10KB分块大小 context.Response.ClearContent(); context.Response.ClearHeaders(); context.Response.ContentType = "application/octet-stream"; context.Response.AddHeader("Content-Disposition", "attachment; filename=AA_GS_BUILD.bin"); // 关闭响应缓冲,写入数据立即发送到客户端 context.Response.BufferOutput = false; // 用文件流逐块读取,服务端也不需要加载整个文件到内存 using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { var buffer = new byte[chunkSize]; int bytesRead; while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) { // 只写入实际读取到的字节数,避免最后一块附带多余空字节 context.Response.BinaryWrite(buffer.Take(bytesRead).ToArray()); context.Response.Flush(); } } context.Response.End(); } public string GettingExtension(string rawUrl) { return rawUrl.Substring(rawUrl.LastIndexOf(".", StringComparison.Ordinal)); } }
客户端侧必须满足的实现要求
仅修改服务端无法完全解决问题,客户端必须配合实现流读取逻辑:
- 禁止使用一次性读取完整响应的API,例如
WebClient.DownloadData、HttpClient.GetByteArrayAsync等,这类API会将整个响应加载到内存中 - 必须获取响应的原始流,每次从流中读取最多10KB的数据,读取到的内容直接写入本地文件,不要临时存放到内存的全局字节数组中
- 参考C#客户端实现:
using (var client = new HttpClient()) using (var responseStream = await client.GetStreamAsync("你的下载接口地址")) using (var fileStream = new FileStream("本地保存路径.bin", FileMode.Create, FileAccess.Write)) { var buffer = new byte[10 * 1024]; int bytesRead; while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { await fileStream.WriteAsync(buffer, 0, bytesRead); } }
内容的提问来源于stack exchange,提问作者shujaat siddiqui
相关产品推荐
相关产品推荐

