HttpClient是下载文件的现代首选,需单例复用、设置超时、用ResponseHeadersRead避免内存溢出、分块读写并支持IProgress进度报告,同时处理重定向、认证、异常和完整性校验。
用 HttpClient 下载文件最稳妥
直接用 WebClient 虽然简单,但已标记为过时(.NET Core 5+ 开始不推荐),HttpClient 是现代首选。它支持异步、可复用、能控制超时和请求头,还天然适配 Progress 实时反馈进度。
关键点:
-
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 才行
HttpClient 本身不提供下载进度回调,得手动分块读取并报告。常见错误是试图在 CopyToAsync 里塞回调——它不支持。
正确做法是用循环 + streamToReadFrom.ReadAsync 读固定大小缓冲区,每读一块就调用 progress.Report():
- 缓冲区大小建议设为 8192 或 65536,太小影响性能,太大占内存
- 注意最后一批数据可能不足缓冲区长度,别直接用
buffer.Length当实际字节数 - 进度百分比要基于
response.Content.Headers.ContentLength计算,但该值可能为null(服务器未返回Content-Length)
若 ContentLength 缺失,只能用“已下载字节数”做相对进度,或改用 WebClient.DownloadFileAsync(仅限 .NET Framework 兼容场景)。