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

如何使用WebClient计算并格式化下载文件的剩余时间?

下载剩余时间计算异常问题修复

问题描述

我编写的文件剩余下载时间计算代码存在异常:

  • 标签多数时候显示00:00:00,仅最右侧秒数在1-2之间快速跳动,疑似毫秒级频繁更新;
  • 文件下载至接近一半时,标签持续显示00:00:00直到下载完成,完全无法准确估算1GB文件的真实剩余时间。

期望实现类似倒计时的美观格式,准确显示剩余下载时间。以下是完整计算逻辑代码:

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    double bytesIn = e.BytesReceived;
    double totalBytes = e.TotalBytesToReceive;
    double percentage = bytesIn / totalBytes * 100;

    // 更新进度条
    progressBar1.Value = (int)Math.Truncate(percentage);

    // 计算下载速度
    DateTime now = DateTime.Now;
    TimeSpan timeElapsed = now - lastProgressUpdateTime;

    if (timeElapsed.TotalMilliseconds > 500)  // 每500毫秒更新一次速度
    {
        long bytesSinceLastUpdate = e.BytesReceived - lastProgressUpdateBytes;
        double speedInBytesPerSecond = bytesSinceLastUpdate / timeElapsed.TotalSeconds;

        // 修复速度为负的问题
        if (speedInBytesPerSecond < 0)
        {
            speedInBytesPerSecond = 0;
        }

        // 显示下载速度
        lblSpeedReporter.Text = FormatBytes(speedInBytesPerSecond) + "/s";

        lastProgressUpdateTime = now;
        lastProgressUpdateBytes = e.BytesReceived;
    }

    // 显示已下载/总大小
    lblSizeReporter.Text = string.Format("{0} / {1}", FormatBytes(bytesIn), FormatBytes(totalBytes));

    // 计算剩余时间
    if (e.BytesReceived > 0 && e.BytesReceived < e.TotalBytesToReceive)
    {
        double downloadRate = e.BytesReceived / timeElapsed.TotalSeconds;
        double remainingBytes = totalBytes - bytesIn;
        double timeLeftInSeconds = remainingBytes / downloadRate;

        if (timeLeftInSeconds > 0)
        {
            TimeSpan timeLeft = TimeSpan.FromSeconds(timeLeftInSeconds);

            // 显示剩余时间
            lblTimeLeftReporter.Text = string.Format("{0:D2}:{1:D2}:{2:D2}", timeLeft.Hours, timeLeft.Minutes, timeLeft.Seconds);
        }
    }
}

问题根源

剩余时间计算的核心错误:

  • 用e.BytesReceived / timeElapsed.TotalSeconds计算下载速率,其中e.BytesReceived是累计下载总量,而非最近500毫秒内的下载量,导致速率计算完全失真;
  • 每次进度变更都触发剩余时间计算,但timeElapsed仅在满足500毫秒条件时才更新,时间间隔异常进一步导致剩余时间混乱。

修复方案

复用已计算的speedInBytesPerSecond(真实的近期下载速率)计算剩余时间,且仅在更新速率时同步更新剩余时间,避免频繁刷新导致的跳变:

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    double bytesIn = e.BytesReceived;
    double totalBytes = e.TotalBytesToReceive;
    double percentage = bytesIn / totalBytes * 100;

    // 更新进度条
    progressBar1.Value = (int)Math.Truncate(percentage);

    // 计算下载速度
    DateTime now = DateTime.Now;
    TimeSpan timeElapsed = now - lastProgressUpdateTime;
    double currentSpeed = 0;

    if (timeElapsed.TotalMilliseconds > 500)  // 每500毫秒更新速度和剩余时间
    {
        long bytesSinceLastUpdate = e.BytesReceived - lastProgressUpdateBytes;
        currentSpeed = bytesSinceLastUpdate / timeElapsed.TotalSeconds;

        // 修复速度为负的问题
        if (currentSpeed < 0)
        {
            currentSpeed = 0;
        }

        // 显示下载速度
        lblSpeedReporter.Text = FormatBytes(currentSpeed) + "/s";

        lastProgressUpdateTime = now;
        lastProgressUpdateBytes = e.BytesReceived;

        // 计算并更新剩余时间
        if (bytesIn > 0 && bytesIn < totalBytes && currentSpeed > 0)
        {
            double remainingBytes = totalBytes - bytesIn;
            double timeLeftInSeconds = remainingBytes / currentSpeed;
            TimeSpan timeLeft = TimeSpan.FromSeconds(timeLeftInSeconds);

            // 智能格式化显示,自动隐藏前导零
            string timeLeftText;
            if (timeLeft.Hours > 0)
            {
                timeLeftText = string.Format("{0:D2}:{1:D2}:{2:D2}", timeLeft.Hours, timeLeft.Minutes, timeLeft.Seconds);
            }
            else if (timeLeft.Minutes > 0)
            {
                timeLeftText = string.Format("{0:D2}:{1:D2}", timeLeft.Minutes, timeLeft.Seconds);
            }
            else
            {
                timeLeftText = string.Format("00:{0:D2}", timeLeft.Seconds);
            }

            lblTimeLeftReporter.Text = timeLeftText;
        }
        else
        {
            lblTimeLeftReporter.Text = "--:--:--";
        }
    }

    // 显示已下载/总大小
    lblSizeReporter.Text = string.Format("{0} / {1}", FormatBytes(bytesIn), FormatBytes(totalBytes));
}

额外优化点

  1. 降低刷新频率:仅在更新速率时计算剩余时间,避免UI频繁跳动;
  2. 智能格式化:根据剩余时间长度自动调整显示格式,更符合用户阅读习惯;
  3. 边界处理:速率为0或下载未开始/已完成时,显示--:--:--替代无效的0值;
  4. 稳定速率计算:复用基于近期下载量的速率值,更能反映当前真实下载速度。

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 17:20:28