项目里要下载一个 2G 的安装包,同事用 WebClient.DownloadFile 一把梭,结果内存直接飙到 1.8G,进程被系统干掉。WebClient 在 .NET 6 之后已经标记过时,大文件更不该用它。
换 HttpClient,关键是别把整个响应读进内存。用 ResponseHeadersRead 模式拿到流,再一段段写进 FileStream。这样内存占用跟文件大小无关,基本稳定在几兆。
带进度和超时的写法
核心几行是这样:
using var resp = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);using var fs = File.Create(path);await resp.Content.CopyToAsync(fs);要进度,就自己读流。循环里每次读 81920 字节,累加已读长度,除以总长度算出百分比,回调给界面。注意进度回调别在界面线程里直接更新控件,用 Invoke 切回去。
断点续传靠 Range 头
文件下到一半断网,重下从零开始很浪费。做法是先看本地文件大小,请求时带上 Range 头,从已下载长度开始要。服务端返回 206 就接着写,返回 200 说明不支持,只能重下。
写文件用 FileMode.Append,别用 Create,否则之前下的部分会被清空。
几个容易忽略的点
一是超时。HttpClient 默认 100 秒,大文件在慢网下必然超时。要把 Timeout 设成 InfiniteTimeSpan,再用 CancellationToken 自己控制。二是证书,内网自签证书会报错,别图省事全局关掉校验。
三是释放。HttpClient 建议做成单例,反复 new 会耗光 socket。文件流用 using 包住,异常路径也要保证关掉,不然文件被占用,下次下载直接失败。