Range请求头需设为"bytes=A-B"格式且服务端必须支持206响应,C#中用HttpClient需配合FileInfo.Length获取偏移、FileMode.Append打开文件、校验Content-Range头才能实现可靠断点续传。

Range 请求头怎么设才有效
服务器必须支持 206 Partial Content,否则 Range 头会被忽略,返回完整文件和 200 OK。C# 里用 HttpClient 发请求时,不能只加 Header 就完事——得确保服务端真的开了分块支持(比如 Nginx 需要 accept_ranges on;,IIS 默认开,ASP.NET Core 静态文件中间件默认支持)。
实操建议:
- 先手动用
curl -v -H "Range: bytes=0-1023" https://example.com/file.zip测试响应头是否含Accept-Ranges: bytes和状态码是否为206 - C# 中设置:
client.DefaultRequestHeaders.Add("Range", "bytes=1024-2047"),注意格式必须是bytes=A-B,不能写成bytes=A-(末尾不指定则表示“从 A 到末尾”,但部分服务端不认) - 如果服务端返回
416 Requested Range Not Satisfiable,说明已传完或范围越界,不是代码问题,要检查本地已写入字节数是否和服务器文件大小一致
HttpClient 下载中断后怎么续传
断点续传本质是「本地已存文件长度」和「HTTP Range 起始偏移」对齐。C# 没有内置“续传”方法,全靠自己控制流和文件指针。
实操建议:
- 下载前用
new FileInfo(path).Length获取本地已有字节数,作为Range的起始值(比如 12345 →"bytes=12345-") - 用
File.Open(path, FileMode.Append, FileAccess.Write)打开文件,避免覆盖;别用FileMode.Create - 响应体要用
HttpContent.ReadAsStreamAsync()拿到流,再用Stream.CopyToAsync(fileStream)写入,不要转成byte[],大文件会爆内存 - 记得在写入前校验服务端返回的
Content-Range头,确认起始位置匹配,否则可能是服务端没按 Range 返回(比如反向代理缓存了全量响应)
怎么判断断点续传是否真正生效
光看状态码是 206 不够,还得核对三个关键值是否闭环:本地文件长度、请求 Range 起始、响应 Content-Range 中的起始字节。三者不等,说明续传逻辑漏了某环。
常见错误现象:
- 每次重启都重下——
FileInfo.Length拿错文件,或文件被其他进程锁住导致长度读为 0 - 下载完文件损坏——没校验
Content-Range,服务端实际返回了0-1023却写到了文件末尾 - 进度条倒退——把
Content-Length(当前块大小)误当成总大小,正确总大小应从Content-Range的/N部分提取,如bytes 1024-2047/1048576中的1048576
并发多个 Range 请求能加速吗
理论上可以,但 C# 默认 HttpClient 对同一域名有连接数限制(.NET 5+ 默认 10,旧版 2),且多数 CDN 或 Web 服务器会限制单 IP 并发 Range 数(比如 Nginx 的 limit_conn)。盲目拆分反而触发限流或 429。
实操建议:
- 单文件优先走单连接 + 分块读取,更稳;真要并发,用不同
HttpClient实例(注意 DNS 缓存和连接池竞争) - 每段 Range 需独立计算偏移和长度,写入时必须用
FileStream.Position定位,不能依赖顺序写入——否则多线程写会错位 - 小文件(100MB)且服务端明确支持高并发 Range 时,再考虑最多 3–4 段并行
断点续传真正的复杂点不在 C# 代码本身,而在服务端行为不可控——比如代理层吞掉 Range、CDN 缓存策略干扰、甚至同一 URL 在不同节点返回不同 ETag 导致续传时校验失败。上线前务必在真实部署环境跑通端到端链路。











