You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 20:47:39