判断断点续传支持需先发HEAD请求检查Accept-Ranges: bytes及Content-Length;HttpClient需手动设Range头并以FileMode.Append写入,响应206为成功,200或416需对应处理;重试仅针对网络异常,须配合指数退避与文件校验。

下载中断后如何判断是否支持断点续传
关键看服务端是否返回 Accept-Ranges: bytes 响应头,且客户端发起请求时带了 Range 头。如果服务端不支持,HttpWebRequest 或 HttpClient 发起带 Range 的请求会直接返回 416(Requested Range Not Satisfiable)或 200 但重传整个文件——后者容易误以为“续传成功”,实则浪费流量。
实操建议:
- 先发一个 HEAD 请求,检查响应头中是否存在
Accept-Ranges且值为bytes - 同时读取
Content-Length,确认文件总大小,用于后续校验和分块计算 - 若服务端返回
Accept-Ranges: none或无该头,必须放弃断点逻辑,改用完整重试 + 文件覆盖
HttpClient 实现带 Range 的续传下载
HttpClient 本身不自动处理断点,需手动构造 Range 请求头,并确保目标文件以 FileMode.Append 打开(注意:不是 FileMode.Create)。
常见错误现象:文件越下越大但内容错乱,本质是重复写入了开头部分——因为没跳过已下载字节。
实操建议:
- 用
FileInfo.Length获取已存在文件大小,作为Range起始位置:bytes={existingLength}- - 创建
HttpRequestMessage,设置Method = HttpMethod.Get,再添加headers.Add("Range", $"bytes={existingLength}-") - 用
FileStream以FileMode.Append和FileAccess.Write打开文件,避免覆盖已有数据 - 务必检查响应状态码:206 表示成功续传;200 表示服务端忽略 Range(需清空文件重下);416 表示已下载完成(可直接退出)
重试策略要区分网络错误与业务错误
不是所有失败都该重试。比如 404、403、401 等 HTTP 状态码,重试毫无意义;而 HttpRequestException、TaskCanceledException(超时)、底层 socket 中断才需要重试。
性能与体验平衡点:
- 建议最多重试 3 次,间隔用指数退避(如 1s → 2s → 4s),避免雪崩式重连
- 每次重试前重新检查本地文件长度和服务端
Content-Length,防止服务端文件被替换导致续传错位 - 把重试逻辑封装进独立方法,传入当前已下载字节数、
HttpClient实例和取消令牌,便于单元测试
文件完整性校验不能省略
断点续传过程中,磁盘写入异常、缓存未刷盘、杀进程等都可能导致文件末尾损坏,仅靠长度一致无法保证正确性。
最容易被忽略的点:
- 下载完成后,必须用服务端提供的
ETag或Content-MD5校验(如有);没有的话,至少计算本地文件的SHA256并与服务端 API 返回的哈希比对 - 不要在下载中途频繁计算全量哈希——性能差;可在最后一步一次性校验
- 校验失败时,删除不完整文件,从头开始下载(此时应记录日志,避免无限循环)
断点续传真正难的不是代码写几行,而是状态同步:本地文件长度、服务端实际内容、网络中间件(如代理、CDN)是否透传 Range、甚至杀毒软件临时锁定文件都会破坏一致性。每一步都要有 fallback 和可观测性,比如记录每次请求的 Range 值、响应状态码、写入字节数。









