C#如何实现文件下载进度条显示并获取下载进度

来源:建站技术作者:本地能跑头衔:程序员
导读:本期聚焦于小伙伴创作的《C#如何实现文件下载进度条显示并获取下载进度》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何实现文件下载进度条显示并获取下载进度》有用,将其分享出去将是对创作者最好的鼓励。

在C#开发桌面应用或后台服务时,实现文件下载功能的同时显示实时进度条,需要准确获取已下载的字节数,再结合文件总大小计算下载百分比,最后将进度同步到UI控件上。下面以HttpClient为基础,详细讲解完整的实现方案。

C#如何实现文件下载进度条显示并获取下载进度

核心实现思路

整个流程可以分为三个步骤:首先发起HTTP请求获取文件的总大小,然后以流的方式读取下载内容,实时记录已下载的字节数,最后计算下载百分比并触发进度更新事件,由UI层监听事件更新进度条。

获取文件总大小

在下载开始前,先通过HTTP HEAD请求获取响应头中的Content-Length字段,得到文件的总字节数,用于后续进度计算。

using System;
using System.Net.Http;

public class FileDownloadHelper
{
    private readonly HttpClient _httpClient;

    public FileDownloadHelper()
    {
        _httpClient = new HttpClient();
    }

    /// <summary>
    /// 获取文件总大小
    /// </summary>
    /// <param name="fileUrl">文件下载地址</param>
    /// <returns>文件总字节数,获取失败返回-1</returns>
    public async Task<long> GetFileTotalSizeAsync(string fileUrl)
    {
        try
        {
            // 发送HEAD请求,只获取响应头不获取响应体
            var request = new HttpRequestMessage(HttpMethod.Head, fileUrl);
            var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            response.EnsureSuccessStatusCode();
            // 从响应头获取Content-Length
            if (response.Content.Headers.ContentLength.HasValue)
            {
                return response.Content.Headers.ContentLength.Value;
            }
            return -1;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"获取文件大小失败:{ex.Message}");
            return -1;
        }
    }
}

实现带进度的文件下载

接下来实现核心的下载方法,通过流读取的方式逐段获取下载内容,每读取一段就更新已下载字节数,计算进度并触发事件。

using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class FileDownloadHelper
{
    private readonly HttpClient _httpClient;
    // 定义进度更新委托,参数为已下载字节数、总字节数、下载百分比
    public delegate void ProgressChangedHandler(long downloadedBytes, long totalBytes, double progressPercentage);
    // 进度更新事件
    public event ProgressChangedHandler OnProgressChanged;

    public FileDownloadHelper()
    {
        _httpClient = new HttpClient();
    }

    /// <summary>
    /// 下载文件并报告进度
    /// </summary>
    /// <param name="fileUrl">文件下载地址</param>
    /// <param name="savePath">本地保存路径</param>
    /// <param name="totalSize">文件总大小,可通过GetFileTotalSizeAsync获取</param>
    /// <param name="cancellationToken">取消令牌</param>
    public async Task DownloadFileWithProgressAsync(string fileUrl, string savePath, long totalSize, CancellationToken cancellationToken = default)
    {
        long downloadedBytes = 0;
        // 发送GET请求,获取响应流
        var response = await _httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
        response.EnsureSuccessStatusCode();
        // 创建本地文件流
        using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
        using (var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken))
        {
            var buffer = new byte[8192]; // 8KB缓冲区
            int bytesRead;
            while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
            {
                // 写入本地文件
                await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
                downloadedBytes += bytesRead;
                // 计算下载百分比
                double progressPercentage = totalSize > 0 ? (double)downloadedBytes / totalSize * 100 : 0;
                // 触发进度更新事件
                OnProgressChanged?.Invoke(downloadedBytes, totalSize, progressPercentage);
            }
        }
    }
}

UI层进度条更新示例

WinForm场景

WinForm中需要在UI线程更新ProgressBar控件,注意跨线程访问控件的线程安全问题。

using System;
using System.Threading;
using System.Windows.Forms;

public partial class MainForm : Form
{
    private FileDownloadHelper _downloadHelper;
    private CancellationTokenSource _cts;

    public MainForm()
    {
        InitializeComponent();
        _downloadHelper = new FileDownloadHelper();
        // 订阅进度更新事件
        _downloadHelper.OnProgressChanged += DownloadHelper_OnProgressChanged;
    }

    private async void btnStartDownload_Click(object sender, EventArgs e)
    {
        string fileUrl = "https://ipipp.com/test/file.zip";
        string savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.zip");
        _cts = new CancellationTokenSource();
        try
        {
            // 先获取文件总大小
            long totalSize = await _downloadHelper.GetFileTotalSizeAsync(fileUrl);
            if (totalSize <= 0)
            {
                MessageBox.Show("无法获取文件大小,将不显示精确进度");
            }
            // 开始下载
            await _downloadHelper.DownloadFileWithProgressAsync(fileUrl, savePath, totalSize, _cts.Token);
            MessageBox.Show("下载完成");
        }
        catch (OperationCanceledException)
        {
            MessageBox.Show("下载已取消");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"下载失败:{ex.Message}");
        }
    }

    private void DownloadHelper_OnProgressChanged(long downloadedBytes, long totalBytes, double progressPercentage)
    {
        // 跨线程更新UI,使用Invoke
        if (progressBar1.InvokeRequired)
        {
            progressBar1.Invoke(new Action<long, long, double>(DownloadHelper_OnProgressChanged), downloadedBytes, totalBytes, progressPercentage);
            return;
        }
        // 更新进度条,progressBar1的Maximum设置为100
        progressBar1.Value = (int)Math.Round(progressPercentage);
        // 更新进度文本
        lblProgress.Text = $"已下载:{downloadedBytes}/{totalBytes} 字节,进度:{progressPercentage:F2}%";
    }

    private void btnCancelDownload_Click(object sender, EventArgs e)
    {
        _cts?.Cancel();
    }
}

WPF场景

WPF中可以使用Dispatcher更新UI,也可以结合MVVM模式通过属性绑定更新进度条。

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Windows;

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private FileDownloadHelper _downloadHelper;
    private CancellationTokenSource _cts;
    private double _downloadProgress;

    public double DownloadProgress
    {
        get { return _downloadProgress; }
        set
        {
            _downloadProgress = value;
            OnPropertyChanged();
        }
    }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        _downloadHelper = new FileDownloadHelper();
        _downloadHelper.OnProgressChanged += DownloadHelper_OnProgressChanged;
    }

    private async void btnStartDownload_Click(object sender, RoutedEventArgs e)
    {
        string fileUrl = "https://ipipp.com/test/file.zip";
        string savePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.zip");
        _cts = new CancellationTokenSource();
        try
        {
            long totalSize = await _downloadHelper.GetFileTotalSizeAsync(fileUrl);
            await _downloadHelper.DownloadFileWithProgressAsync(fileUrl, savePath, totalSize, _cts.Token);
            MessageBox.Show("下载完成");
        }
        catch (OperationCanceledException)
        {
            MessageBox.Show("下载已取消");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"下载失败:{ex.Message}");
        }
    }

    private void DownloadHelper_OnProgressChanged(long downloadedBytes, long totalBytes, double progressPercentage)
    {
        // WPF中使用Dispatcher更新UI
        Dispatcher.Invoke(() =>
        {
            DownloadProgress = progressPercentage;
        });
    }

    private void btnCancelDownload_Click(object sender, RoutedEventArgs e)
    {
        _cts?.Cancel();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

注意事项

  • 如果服务器不支持HEAD请求或者没有返回Content-Length,进度百分比会显示为0,此时可以只显示已下载字节数,不计算百分比。
  • 下载大文件时建议设置合理的缓冲区大小,默认8KB是比较通用的选择,也可以根据网络情况调整。
  • 取消下载时通过CancellationToken传递取消信号,避免资源泄漏。
  • 异步方法中不要使用async void,除了UI事件处理方法,其他场景建议使用async Task返回。

C#文件下载下载进度获取进度条显示HttpClient异步编程修改时间:2026-07-01 05:24:22

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。