c# 如何实现断点续传

来源:这里教程网 时间:2026-02-21 17:38:19 作者:

什么是断点续传的核心机制

断点续传不是某个现成 API,而是靠 HTTP 协议的

Range
请求头 + 服务端支持 + 客户端本地文件偏移写入协同实现的。关键在于:客户端要能告诉服务端“我要从第 N 字节开始下载”,服务端得返回
206 Partial Content
,客户端再把响应体追加写入到本地文件对应位置。

C# 中用
HttpClient
发起带 Range 的请求

必须手动设置

Range
头,并检查响应状态码是否为
206
;不能用
DownloadFileAsync
这类封装方法,它不支持断点控制。

HttpClient
实例建议复用(避免 socket 耗尽),且需启用
AllowAutoRedirect = false
,防止重定向丢失
Range
请求前用
FileInfo.Length
获取已下载字节数,作为
Range: bytes={length}-
响应中通过
response.Content.Headers.ContentRange
解析实际返回的字节范围,验证是否匹配预期
var client = new HttpClient { AllowAutoRedirect = false };
var req = new HttpRequestMessage(HttpMethod.Get, "https://example.com/large.zip");
req.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(localFileLength, null);
<p>var res = await client.SendAsync(req);
if (res.StatusCode != HttpStatusCode.PartialContent) {
throw new InvalidOperationException($"Expected 206, got {res.StatusCode}");
}

如何安全地追加写入已存在文件

直接

FileMode.Append
不行——它会在文件末尾写入,但服务端返回的可能是中间某段(比如断点在 10MB,服务端返回的是 10MB–15MB),必须按字节偏移写入。

打开文件用
FileMode.Open
+
FileAccess.Write
调用
stream.Seek(offset, SeekOrigin.Begin)
定位到指定位置
stream.Write(buffer, 0, read)
写入,而非
WriteAsync
(避免因异步调度导致 seek 位置错乱)
务必用
using
try/finally
确保流关闭,否则下次打开会报“文件正由另一进程使用”

服务端不支持 Range 时怎么降级处理

遇到

416 Range Not Satisfiable
或直接返回
200
(非
206
),说明服务端不支持断点续传。此时有两种选择:

清空本地文件,重新下载(最简单,但浪费已下载数据) 先 HEAD 请求获取
Content-Length
,对比本地文件大小;若一致则跳过下载,不一致才全量重下
注意:某些 CDN 或反向代理会吞掉
Range
头并返回完整响应,此时
Content-Range
响应头为空,需主动检查

真正麻烦的是中间网络中断后文件损坏却长度恰好匹配——这需要额外做校验(如服务端提供

ETag
或分块哈希),单纯靠长度无法保证一致性。

相关推荐