应使用 httpclient 配合流式读取与 iprogress 主动推送实现可靠下载进度更新,避免 webclient 及其已移除的 downloadprogresschanged 事件;需用临时文件、cancellationtoken 和线程安全调度确保健壮性。

用 HttpClient 下载文件并实时更新进度条
直接用 WebClient 已不推荐,它不支持取消、超时控制弱,且无法在下载过程中精确获取已接收字节数。必须用 HttpClient + HttpContent.ReadAsByteArrayAsync 或流式读取,否则拿不到中间进度。
关键不是“怎么下”,而是“怎么边下边知道下了多少”。核心思路是:不用一次性读完响应体,改用 HttpContent.ReadAsStreamAsync 拿到响应流,再用 Stream.CopyToAsync 配合自定义的进度回调委托。
- 务必设置
HttpClient.Timeout,否则大文件可能卡死无响应 - 不要在 UI 线程直接 await 下载任务,会阻塞界面;要用
await Task.Run(...)或Progress<int></int>跨线程更新进度条 - 服务端必须返回
Content-Length头,否则无法计算百分比(部分 CDN 或动态接口不返回,此时只能显示“下载中”)
Progress<int></int> 怎么安全更新 WinForms 进度条
WinForms 的 ProgressBar.Value 只能在创建它的线程访问,而 Progress<int></int> 的回调默认在 Task 所在线程执行——大概率不是 UI 线程。直接赋值会抛 InvalidOperationException:“线程间操作无效”。
正确做法是让 Progress<int></int> 构造时绑定 UI 同步上下文:
var progress = new Progress<int>(value =>
{
if (progressBar.InvokeRequired)
progressBar.Invoke((MethodInvoker)(() => progressBar.Value = value));
else
progressBar.Value = value;
});</int>
- 别用
BackgroundWorker,它已过时,且 Progress 回调更简洁可控 - 如果用 WPF,直接传
Dispatcher.Invoke或绑定IProgress<int></int>到 ViewModel 即可 - 注意
ProgressBar.Maximum必须提前设为文件总大小(从响应头读Content-Length),不能硬写 100
如何处理分块传输(Transfer-Encoding: chunked)或无 Content-Length
遇到没有 Content-Length 的响应(比如某些 API、PHP 脚本、启用了 chunked 编码的 Nginx),response.Content.Headers.ContentLength 为 null,此时无法预知总大小,也就没法算百分比。
这时只能退化为“已下载字节数”文本提示 + “不确定长度”的进度条样式(设 ProgressBar.Style = ProgressBarStyle.Marquee):
- 仍可用流式读取 + 计数器统计已读字节数,只是不更新
Value,只更新label.Text显示 “已下载 2.4 MB” - 别尝试用
response.Content.Headers.GetValues("Content-Range")推算——它只在断点续传时存在,普通 GET 不带 - 若必须强制显示百分比,可先 HEAD 请求试探
Content-Length,但增加一次网络往返,且部分服务器对 HEAD 返回 405
下载中断、重试与临时文件清理
用户点取消、网络闪断、服务端主动断连都会导致下载中途失败。不处理的话,残留的不完整文件会污染目标目录,下次同名覆盖还可能误判成功。
实操要点:
- 用
CancellationTokenSource控制整个下载流程,UI 上“取消”按钮触发cancellationTokenSource.Cancel() - 下载前生成唯一临时文件名(如
Path.ChangeExtension(urlFileName, ".tmp")),下载完成再File.Move替换原文件 - 异常捕获后检查临时文件是否存在,存在就
File.Delete;别依赖finally块——如果进程被强杀,finally不执行 - 不要用
File.Create开头就清空目标文件——万一后续失败,原始文件已丢
真正难的不是写通下载逻辑,而是应对 Content-Length 缺失、服务端连接复位、用户反复启停、磁盘满等边界情况。这些细节没兜住,用户看到的就是“进度条卡住”或“下了一半却提示成功”。











