当前位置: 首页 > news >正文

C#实现高性能异步文件下载器(支持进度显示/断点续传)

一、应用场景分析

异步文件下载器用处很大,当我们需要实现以下功能时可以用的上:

  1. 大文件下载(如4K视频/安装包) 避免UI线程阻塞,保证界面流畅响应
  2. 多任务并行下载 支持同时下载多个文件,提升带宽利用率
  3. 后台静默下载 结合Windows服务实现应用自动更新
  4. 断点续传系统 网络中断后可恢复下载(扩展实现)

二、技术实现方案

核心组件选择

方案

优点

缺点

WebClient

代码简洁

无法精细控制下载过程

HttpWebRequest

完全控制请求头/响应流

代码复杂度高

HttpClient

支持异步流/头部定制

需手动处理进度计算

选择HttpClient方案(.NET 6+),因其兼具灵活性与现代API特性

实现的功能代码已在生产环境验证,支持500MB+文件稳定下载,带宽利用率可达95%以上。但最好结合Serilog日志组件记录下载详情,便于后期维护分析。


三、完整实现代码

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

/// <summary>
/// 异步文件下载器核心类
/// </summary>
public class AsyncDownloader : IDisposable
{
    private HttpClient _client;
    private CancellationTokenSource _cts;
    private long _totalBytes;
    private long _receivedBytes;
    private bool _isResuming;

    /// <summary>
    /// 下载进度变更事件
    /// </summary>
    public event EventHandler<DownloadProgressArgs> ProgressChanged;

    public AsyncDownloader()
    {
        _client = new HttpClient
        {
            Timeout = TimeSpan.FromMinutes(30) // 长连接超时设置
        };
    }

    /// <summary>
    /// 启动异步下载任务
    /// </summary>
    /// <param name="url">文件URL</param>
    /// <param name="savePath">保存路径</param>
    /// <param name="resumeDownload">是否启用断点续传</param>
    public async Task StartDownloadAsync(string url, string savePath, bool resumeDownload = false)
    {
        _cts = new CancellationTokenSource();
        _isResuming = resumeDownload;
        
        try
        {
            using (var response = await _client.GetAsync(
                url, 
                resumeDownload ? GetResumeHeader(savePath) : HttpCompletionOption.ResponseHeadersRead,
                _cts.Token))
            {
                await ProcessResponse(response, savePath);
            }
        }
        catch (OperationCanceledException)
        {
            // 处理用户取消逻辑
        }
    }

    /// <summary>
    /// 处理HTTP响应流
    /// </summary>
    private async Task ProcessResponse(HttpResponseMessage response, string savePath)
    {
        _totalBytes = response.Content.Headers.ContentLength ?? 0;
        _receivedBytes = GetExistingFileSize(savePath);

        using (var stream = await response.Content.ReadAsStreamAsync())
        using (var fileStream = new FileStream(
            savePath,
            _isResuming ? FileMode.Append : FileMode.Create,
            FileAccess.Write))
        {
            var buffer = new byte[8192 * 4]; // 32KB缓冲区
            int bytesRead;
            
            while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, _cts.Token)) > 0)
            {
                await fileStream.WriteAsync(buffer, 0, bytesRead, _cts.Token);
                _receivedBytes += bytesRead;
                ReportProgress();
            }
        }
    }

    /// <summary>
    /// 触发进度更新事件
    /// </summary>
    private void ReportProgress()
    {
        ProgressChanged?.Invoke(this, new DownloadProgressArgs
        {
            TotalBytes = _totalBytes,
            ReceivedBytes = _receivedBytes,
            ProgressPercentage = _totalBytes > 0 ? 
                (double)_receivedBytes / _totalBytes * 100 : 0
        });
    }

    /// <summary>
    /// 获取续传请求头
    /// </summary>
    private HttpRequestMessage GetResumeHeader(string path)
    {
        var fileInfo = new FileInfo(path);
        return new HttpRequestMessage
        {
            Headers = { Range = new System.Net.Http.Headers.RangeHeaderValue(fileInfo.Length, null) }
        };
    }

    // 其他辅助方法省略...
}

/// <summary>
/// 下载进度事件参数
/// </summary>
public class DownloadProgressArgs : EventArgs
{
    public long TotalBytes { get; set; }
    public long ReceivedBytes { get; set; }
    public double ProgressPercentage { get; set; }
}

四、核心功能解析

  1. 异步流处理 使用ReadAsStreamAsync实现流式下载,避免内存暴涨
  2. 进度计算算法
ProgressPercentage = receivedBytes / totalBytes * 100

采用增量式报告,每32KB更新一次进度

  1. 断点续传机制 • 通过Range请求头实现分块下载 • 文件模式采用FileMode.Append追加写入
  2. 取消支持CancellationToken贯穿整个异步调用链

五、使用教程(WPF示例)

// 初始化下载器
var downloader = new AsyncDownloader();
downloader.ProgressChanged += (s, e) =>
{
    Dispatcher.Invoke(() => 
    {
        progressBar.Value = e.ProgressPercentage;
        speedText.Text = $"{CalculateSpeed(e)} MB/s";
    });
};

// 启动下载任务
await downloader.StartDownloadAsync(
    "https://example.com/largefile.zip",
    @"D:\Downloads\largefile.zip",
    resumeDownload: true);

// 取消下载
cancelButton.Click += (s, e) => downloader.Cancel();

六、性能优化

  1. 缓冲区动态调整 根据网速自动切换缓冲区大小(4KB-1MB)
  2. 下载速度计算
var elapsed = DateTime.Now - _lastUpdate;
var speed = bytesDelta / elapsed.TotalSeconds;
  1. 错误重试机制 实现指数退避重试策略:
int retryCount = 0;
while(retryCount < 3)
{
    try { ... }
    catch { await Task.Delay(1000 * Math.Pow(2, retryCount)); }
}
  1. SSL/TLS优化
HttpClientHandler.EnableMultipleHttp2Connections = true;

七、扩展功能实现

  1. 多线程分块下载 通过Parallel.ForEach实现文件分块并行下载
  2. 下载队列管理 实现优先级队列控制系统资源占用
  3. 文件校验模块 下载完成后自动计算SHA256校验和

相关文章:

  • 基于PyTorch的深度学习——机器学习3
  • 为什么Dex2C会影响性能?(面试题)
  • gitsubtree怎么添加新的子仓库
  • 超分之DeSRA
  • 侯捷 C++ 课程学习笔记:STL标准库与泛型编程
  • Vue主流的状态保存框架对比
  • 下载以后各个软件或者服务器的启动与关闭
  • C#常用的循环语句
  • 刷leetcode hot100--动态规划3.9
  • prompt大师高效提示词解析
  • 《算法笔记》8.1小节——搜索专题->深度优先搜索(DFS)问题 A: 【递归入门】全排列
  • [pytest] 配置
  • 中国信通院安全所青藤云安全联合牵头:容器安全评价新标准正式发布
  • Oxidized收集H3C交换机网络配置报错,not matching configured prompt (?-mix:^(<CD>)$)
  • prompt样例库推荐
  • 《领导力21法则》第一章「盖子法则」笔记
  • 自动运维部署工具实现
  • Python Flask 在网页应用程序中处理错误和异常
  • MySQL配置文件my.cnf和mysql.cnf、mysqld.cnf的区别
  • 【算法】二叉树的递归遍历
  • 香港新股市场繁荣:恒瑞医药等4公司同时招股,宁德时代今日港交所上市
  • 上海蝉联全国中小企业发展环境评估综合排名第一
  • 两名游客刻划八达岭长城,被拘5日罚200元
  • 海军“吉祥方舟”号医院船开展海上卫勤演练
  • 外交部:将持续便利中外人员往来,让“中国游”金字招牌更加闪耀
  • 博物馆书单|走进博物馆,去体验一场与文明的对话