C#中HttpClient下载进度无法获取问题及替代方案求助
问题分析与解决方案
你的代码问题根源
你创建了两个独立的ProgressMessageHandler实例:一个传给了HttpClient用于发起请求,另一个绑定了HttpReceiveProgress事件但从未被使用。两者完全没有关联,自然不会触发进度回调。
方案一:修复ProgressMessageHandler的正确用法
给同一个ProgressMessageHandler实例绑定事件,并确保使用SendAsync方法发起请求(配合ResponseHeadersRead选项,让进度回调逐步触发):
string textDownloadProgress; private async void DownloadFileAsync() { string filename = "file.zip"; var httpHandler = new HttpClientHandler() { AllowAutoRedirect = true }; // 仅创建一个ProgressMessageHandler实例 var progressHandler = new ProgressMessageHandler(httpHandler); // 绑定进度事件 progressHandler.HttpReceiveProgress += (_, e) => { if (e.TotalBytes.HasValue) { // 计算并更新进度(UI线程需注意线程安全,比如用Invoke/Dispatcher) textDownloadProgress = $"当前进度:{e.ProgressPercentage:F2}%"; } else { textDownloadProgress = $"已下载:{e.BytesTransferred}字节"; } }; // 使用绑定了事件的handler初始化HttpClient using var client = new HttpClient(progressHandler) { Timeout = Timeout.InfiniteTimeSpan }; var request = new HttpRequestMessage(HttpMethod.Get, "URL"); // 用SendAsync并指定ResponseHeadersRead,确保逐步接收响应体触发进度 using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); using var filestream = new FileStream(filename, FileMode.Create); using var netstream = await response.Content.ReadAsStreamAsync(); await netstream.CopyToAsync(filestream); }
方案二:手动实现进度跟踪(更灵活可控)
如果不想依赖ProgressMessageHandler,可以手动分块读取流,自己计算并触发进度更新,这种方式适配更多场景:
private async void DownloadFileAsync() { string filename = "file.zip"; // 建议HttpClient全局单例,避免频繁创建耗尽socket资源 using var client = new HttpClient() { Timeout = Timeout.InfiniteTimeSpan }; var request = new HttpRequestMessage(HttpMethod.Get, "URL"); using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); long totalBytes = response.Content.Headers.ContentLength ?? -1; long downloadedBytes = 0; // 8KB缓冲区,可根据需求调整大小 var buffer = new byte[8192]; using var filestream = new FileStream(filename, FileMode.Create); using var netstream = await response.Content.ReadAsStreamAsync(); int bytesRead; while ((bytesRead = await netstream.ReadAsync(buffer, 0, buffer.Length)) > 0) { await filestream.WriteAsync(buffer, 0, bytesRead); downloadedBytes += bytesRead; if (totalBytes > 0) { double progress = (double)downloadedBytes / totalBytes * 100; textDownloadProgress = $"下载进度:{progress:F2}%"; } else { textDownloadProgress = $"已下载:{downloadedBytes}字节"; } } }
关键注意事项
- 如果是在UI项目(WinForms/WPF)中更新进度,必须确保回调在UI线程执行,否则会抛出跨线程异常,可使用
Control.Invoke(WinForms)或Dispatcher.Invoke(WPF)切换线程。 HttpClient建议全局单例复用,不要每次下载都创建新实例,避免系统socket资源耗尽。
内容的提问来源于stack exchange,提问作者Varion Drakonov
相关产品推荐
相关产品推荐

