应避免在UI线程调用WebClient.DownloadFile,改用DownloadFileAsync或HttpClient+流式下载;需设置User-Agent防403;下载前清理旧文件;HttpClient推荐结合Polly重试。

WebClient.DownloadFile 会阻塞线程,别在 UI 线程直接调用
WebClient 的 DownloadFile 是同步方法,调用时当前线程会一直等待直到下载完成或失败。在 WinForms 或 WPF 的主线程里直接用,界面立刻卡死。
实操建议:
- 改用
DownloadFileAsync,配合DownloadProgressChanged和DownloadFileCompleted事件更新进度和处理结果 - 如果用的是 .NET Core / .NET 5+,优先转向
HttpClient+GetStreamAsync配合FileStream流式写入,更可控且默认异步 - 注意
DownloadFileAsync不支持取消,如需中途停止,得换HttpClient+CancellationToken
HttpClient 下载大文件必须流式处理,不能用 GetStringAsync
GetStringAsync 会把整个响应体读进内存再转成字符串,下载几 MB 的 PDF 或 ZIP 就可能触发 OutOfMemoryException,尤其在低内存设备上。
正确做法是用流:
- 调用
GetStreamAsync获取响应流,而不是读取全部内容 - 用
FileStream(设置FileOptions.Asynchronous)或FileStream.WriteAsync写入磁盘 - 务必用
using或await using确保流及时释放,否则文件句柄泄漏、磁盘写入不完整
示例关键片段:
using var response = await client.GetAsync(url, cancellationToken); using var streamToRead = await response.Content.ReadAsStreamAsync(); using var streamToWrite = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, FileOptions.Asynchronous); await streamToRead.CopyToAsync(streamToWrite, cancellationToken);
WebClient 默认不带 User-Agent,部分网站会 403 拒绝
很多 CDN 或反爬站点检查 User-Agent 请求头,WebClient 默认不发送,直接返回 403。这不是权限问题,是服务端主动拦截。
解决方式:
- 给
WebClient.Headers显式添加:client.Headers[HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" -
HttpClient则通过DefaultRequestHeaders.UserAgent.ParseAdd(...)设置 - 注意某些站点还校验
Accept或Referer,遇到 403 时用浏览器开发者工具比对请求头
下载中断或网络异常时,临时文件没清理干净
无论是 WebClient.DownloadFileAsync 还是手写的 HttpClient 流写入,一旦中途出错(如断网、服务器关闭连接),目标路径可能残留一个不完整的文件。下次再下载同名文件,不会自动覆盖,而是直接写入——导致文件损坏却无提示。
安全做法:
- 下载前先检查目标路径是否存在,存在则
File.Delete(确保有权限) - 或改用临时文件名下载,成功后再
File.Move覆盖,利用原子操作避免脏数据暴露 -
WebClient没内置重试,HttpClient可配合Polly实现带退避的重试逻辑,但注意幂等性:GET 一般安全,POST 不适用
临时文件残留不是小问题,它会让后续「文件存在判断」逻辑失效,尤其是自动更新场景下静默出错最难排查。










