用 HttpClient
下载文件最稳妥
直接用
WebClient虽然简单,但已标记为过时(.NET Core 5+ 开始不推荐),
HttpClient是现代首选。它支持异步、可复用、能控制超时和请求头,还天然适配
Progress<int></int>实时反馈进度。
关键点:
HttpClient应作为单例或静态实例重用,不要每次下载都 new 一个 务必用
await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead),避免把整个响应体提前读进内存 用
response.Content.ReadAsStreamAsync()获取流后,再配合
FileStream分块写入磁盘 记得设置
client.Timeout,否则大文件可能卡死无响应
示例片段:
var client = new HttpClient();
client.Timeout = TimeSpan.FromMinutes(10);
using var response = await client.GetAsync("https://example.com/file.zip", HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using var streamToReadFrom = await response.Content.ReadAsStreamAsync();
using var streamToWriteTo = File.Create(@"C:\temp\file.zip");
await streamToReadFrom.CopyToAsync(streamToWriteTo);
需要显示进度?绑定 IProgress<int></int>
才行
HttpClient本身不提供下载进度回调,得手动分块读取并报告。常见错误是试图在
CopyToAsync里塞回调——它不支持。
正确做法是用循环 +
streamToReadFrom.ReadAsync读固定大小缓冲区,每读一块就调用
progress.Report(): 缓冲区大小建议设为 8192 或 65536,太小影响性能,太大占内存 注意最后一批数据可能不足缓冲区长度,别直接用
buffer.Length当实际字节数 进度百分比要基于
response.Content.Headers.ContentLength计算,但该值可能为
null(服务器未返回
Content-Length)
若
ContentLength缺失,只能用“已下载字节数”做相对进度,或改用
WebClient.DownloadFileAsync(仅限 .NET Framework 兼容场景)。
处理重定向和认证:别漏掉 AllowAutoRedirect
和 DefaultRequestHeaders
很多下载失败不是因为代码逻辑,而是服务器返回 302 或要求 Bearer Token。默认
HttpClient会自动跟随重定向,但某些私有 API 可能禁用此行为。 如需手动控制跳转,设
client.DefaultRequestHeaders.Referrer或检查
response.RequestMessage.RequestUri是否变化 带 Token 的请求:加
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token)
需要 Cookie 或表单登录态?必须复用同一个 HttpClient实例,并启用
CookieContainer(需搭配
HttpHandler构造)
没设认证头却去请求受保护资源,大概率收到 401;忽略重定向则可能卡在中间页返回 HTML 而非文件流。
大文件或弱网下容易出错:必须加取消令牌和异常分类处理
用户点“取消下载”、网络中断、磁盘满——这些都会抛不同异常,不能全 catch 住吞掉。
所有await调用都要传
cancellationToken,并在 UI 层绑定按钮的取消逻辑
OperationCanceledException表示主动取消,不是错误,别记日志
HttpRequestException包含网络层问题(DNS 失败、连接被拒),可重试 1–2 次
IOException如磁盘空间不足,应提示用户清理而非重试
最容易被忽略的是:下载中途断电或进程被杀,会导致文件不完整却没报错。建议下载完成后用
response.Content.Headers.ContentMD5校验(如果服务器提供了),或至少检查最终文件大小是否匹配
ContentLength。
