You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebAPI大文件批量传输更新机制咨询:单文件1GB/20客户端并发

大文件WebAPI下载的优化方案

嘿,首先得指出你当前代码里的致命问题:你用File.ReadAllBytes(reqFile)把整个1GB的文件直接加载到服务器内存里了,单个请求就占1GB内存,20个并发的话服务器内存直接炸锅,完全扛不住。而且内存流也是多余的,完全可以直接用文件流实现流式传输,不用把整个文件塞进内存。

优化后的基础实现(最省心)

这个版本直接用StreamContent让WebAPI自动处理流式传输,不会一次性加载整个文件到内存:

public HttpResponseMessage GetFile(string name)
{
    // 先做安全校验:只取文件名,防止路径遍历攻击
    var safeFileName = Path.GetFileName(name);
    var reqFile = Path.Combine(@"C:\updates\", safeFileName);
    
    // 先检查文件是否存在,避免抛出异常
    if (!File.Exists(reqFile))
    {
        return Request.CreateResponse(HttpStatusCode.NotFound);
    }
    
    // 打开文件流,允许其他进程同时读取(比如你后台更新文件的时候)
    var fileStream = new FileStream(reqFile, FileMode.Open, FileAccess.Read, FileShare.Read);
    
    var response = Request.CreateResponse(HttpStatusCode.OK);
    // 用StreamContent替代内存流,WebAPI会自动分块发送文件
    response.Content = new StreamContent(fileStream);
    
    // 设置必要的响应头,让客户端能正确处理下载
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    response.Content.Headers.ContentLength = fileStream.Length; // 告诉客户端文件总大小,方便显示进度
    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = safeFileName // 让客户端保存时用这个文件名
    };
    
    return response;
}

进阶:支持断点续传(提升用户体验)

如果你的客户端可能遇到下载中断的情况,支持断点续传会更友好,客户端可以从断点继续下载,不用重新下载整个文件:

public HttpResponseMessage GetFile(string name)
{
    var safeFileName = Path.GetFileName(name);
    var reqFile = Path.Combine(@"C:\updates\", safeFileName);
    
    if (!File.Exists(reqFile))
    {
        return Request.CreateResponse(HttpStatusCode.NotFound);
    }
    
    var fileInfo = new FileInfo(reqFile);
    var response = new HttpResponseMessage();
    var fileStream = new FileStream(reqFile, FileMode.Open, FileAccess.Read, FileShare.Read);
    
    // 处理客户端的Range请求(断点续传的核心)
    if (Request.Headers.Range != null)
    {
        try
        {
            var range = Request.Headers.Range.Ranges.FirstOrDefault();
            if (range == null)
            {
                response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
                return response;
            }
            
            var startByte = range.From ?? 0;
            var endByte = range.To ?? fileInfo.Length - 1;
            
            // 校验请求的范围是否合法
            if (startByte >= fileInfo.Length || endByte >= fileInfo.Length || startByte > endByte)
            {
                response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
                response.Content = new StringContent($"请求的范围无效:{startByte}-{endByte}");
                return response;
            }
            
            // 把文件流定位到起始位置
            fileStream.Seek(startByte, SeekOrigin.Begin);
            var contentLength = endByte - startByte + 1;
            
            response.StatusCode = HttpStatusCode.PartialContent;
            response.Content = new StreamContent(fileStream);
            response.Content.Headers.ContentLength = contentLength;
            response.Content.Headers.ContentRange = new ContentRangeHeaderValue(startByte, endByte, fileInfo.Length);
        }
        catch
        {
            response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
            return response;
        }
    }
    else
    {
        // 普通下载请求,直接返回整个文件
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new StreamContent(fileStream);
        response.Content.Headers.ContentLength = fileInfo.Length;
    }
    
    // 设置通用的响应头
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = safeFileName
    };
    
    return response;
}

额外的几个注意点

  • 路径安全:一定要用Path.GetFileName(name)过滤用户传入的文件名,防止路径遍历攻击(比如用户传入../config/secret.key这种恶意路径)。
  • 并发优化:20个并发不算特别高,但可以考虑把方法改成异步(async Task<HttpResponseMessage>),避免阻塞服务器线程;如果后续并发更高,可以考虑加个简单的并发限制。
  • 缓存控制:如果你的更新文件不会频繁变动,可以添加Cache-Control响应头,让客户端缓存文件,减少重复请求对服务器的压力。

内容的提问来源于stack exchange,提问作者Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:25:05