获取文件大小异常时progressBarEx2显示NaN的问题排查
问题分析
- 无效文件大小导致总进度异常:
GetFileSizeAsync返回-1时直接累加到totalBytesToReceive,导致总和为负数,后续设置progressBarEx2.Maximum为0时,进度计算出现除以0的情况,显示NaN。 - 总进度计算逻辑错误:原代码中
overallProgress = e.BytesReceived + progressBarEx2.Maximum * currentDownloadIndex的逻辑完全错误,progressBarEx2.Maximum是总大小,乘以已完成文件数会导致数值远超实际进度。 - ProgressBar显示限制:WinForms的ProgressBar当
Maximum和Minimum都为0时,无法正常计算进度百分比,会出现异常显示。
修复方案
1. 修正文件大小统计逻辑
- 新增变量记录已完成文件的总大小、未知大小的文件数量,避免无效值干扰总进度计算。
- 仅将有效文件大小(>0)计入总大小,未知大小的文件单独处理。
2. 调整总进度条的Maximum设置
- 若存在有效总大小,设置为该值;若全为未知大小,将Maximum设置为文件总数,按完成文件数展示进度。
3. 修正总进度计算逻辑
- 维护已完成文件的总大小,总进度为已完成大小加上当前文件已下载大小;若总大小未知,则按已完成文件数占总文件数的比例计算进度。
4. 处理下载完成后的真实大小更新
- 在文件下载完成后,若之前获取的大小无效,用实际下载的文件大小更新总进度数据。
修改后的完整代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Windows.Forms; namespace Download_Webclient { public partial class Form1 : Form { private readonly Stopwatch stopwatch = new Stopwatch(); private List<string> downloadLinks; private int currentDownloadIndex; private long totalValidBytes; // 仅统计有效文件大小(>0) private long completedTotalBytes; // 已完成文件的总大小 private int unknownSizeFileCount; // 未知大小的文件数量 public Form1() { InitializeComponent(); downloadLinks = new List<string> { "https://hil-speed.hetzner.com/100MB.bin", "https://hil-speed.hetzner.com/100MB.bin", "https://hil-speed.hetzner.com/100MB.bin", "https://hil-speed.hetzner.com/100MB.bin", // 可添加更多链接 }; } private async void btnDownload_Click(object sender, EventArgs e) { using (var folderDialog = new FolderBrowserDialog()) { DialogResult result = folderDialog.ShowDialog(); if (result == DialogResult.OK) { btnDownload.Enabled = false; progressBarEx1.Value = 0; progressBarEx2.Value = 0; totalValidBytes = 0; unknownSizeFileCount = 0; completedTotalBytes = 0; currentDownloadIndex = 0; // 预先统计有效文件大小和未知文件数 List<long> fileSizes = new List<long>(); foreach (string downloadLink in downloadLinks) { Uri uri = new Uri(downloadLink); long size = await GetFileSizeAsync(uri); fileSizes.Add(size); if (size > 0) { totalValidBytes += size; } else { unknownSizeFileCount++; } } // 设置总进度条的最大值 if (totalValidBytes > 0) { progressBarEx2.Maximum = (int)totalValidBytes; } else { // 全为未知大小,按文件数量设置进度 progressBarEx2.Maximum = downloadLinks.Count; progressBarEx2.Minimum = 0; } // 开始逐个下载 for (int i = 0; i < downloadLinks.Count; i++) { string downloadLink = downloadLinks[i]; long fileSize = fileSizes[i]; Uri uri = new Uri(downloadLink); string fileName = System.IO.Path.Combine(folderDialog.SelectedPath, System.IO.Path.GetFileName(uri.LocalPath)); using (WebClient wc = new WebClient()) { // 捕获当前文件的大小,用于更新总进度 long currentFileSize = fileSize; wc.DownloadProgressChanged += (s, eArgs) => OnDownloadProgressChange(s, eArgs, currentFileSize); wc.DownloadFileCompleted += (s, eArgs) => OnDownloadFileComplete(s, eArgs, fileName, currentFileSize); stopwatch.Start(); await wc.DownloadFileTaskAsync(uri, fileName); } currentDownloadIndex++; } lblProgress.Text = "所有下载完成!"; AdjustLabelPosition(lblProgress, progressBarEx1); btnDownload.Enabled = true; } } } private async Task<long> GetFileSizeAsync(Uri uri) { try { using (HttpClient httpClient = new HttpClient()) { using (var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, uri))) { if (response.Headers.Contains("Content-Length") && long.TryParse(response.Headers.GetValues("Content-Length").FirstOrDefault(), out long contentLength)) { return contentLength; } } } } catch (HttpRequestException ex) { Console.WriteLine($"获取文件大小出错: {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"发生意外错误: {ex.Message}"); } return -1; } private void OnDownloadProgressChange(object sender, DownloadProgressChangedEventArgs e, long currentFileSize) { // 单个文件进度处理 string downloadProgress = e.ProgressPercentage + "%"; string downloadSpeed = string.Format("{0} MB/s", (e.BytesReceived / 1024.0 / 1024.0 / stopwatch.Elapsed.TotalSeconds).ToString("0.00")); string downloadedMBs = Math.Round(e.BytesReceived / 1024.0 / 1024.0) + " MB"; string totalMBs = Math.Round(e.TotalBytesToReceive / 1024.0 / 1024.0) + " MB"; string progress = $"{downloadedMBs}/{totalMBs} ({downloadProgress}) @ {downloadSpeed}"; lblProgress.Text = progress; AdjustLabelPosition(lblProgress, progressBarEx1); progressBarEx1.Value = e.ProgressPercentage; // 总进度处理 if (totalValidBytes > 0) { // 有已知总大小,计算已完成+当前下载的字节数 long overallProgress = completedTotalBytes + e.BytesReceived; progressBarEx2.Value = (int)Math.Min(totalValidBytes, overallProgress); } else { // 无已知总大小,按已完成文件数+当前文件进度比例计算 int fileProgress = currentDownloadIndex + (int)((double)e.BytesReceived / e.TotalBytesToReceive); progressBarEx2.Value = Math.Min(downloadLinks.Count, fileProgress); } } private void OnDownloadFileComplete(object sender, AsyncCompletedEventArgs e, string fileName, long originalFileSize) { if (e.Error != null) { lblProgress.Text = $"下载文件出错: {e.Error.Message}"; } else { // 更新已完成文件的总大小,如果原大小无效,用实际文件大小 if (originalFileSize > 0) { completedTotalBytes += originalFileSize; } else { completedTotalBytes += new System.IO.FileInfo(fileName).Length; } if (currentDownloadIndex < downloadLinks.Count - 1) { lblProgress.Text = $"正在下载下一个文件... ({currentDownloadIndex + 2}/{downloadLinks.Count})"; } else { lblProgress.Text = "所有下载完成!"; // 最后确保总进度条拉满 progressBarEx2.Value = progressBarEx2.Maximum; } } AdjustLabelPosition(lblProgress, progressBarEx1); stopwatch.Reset(); } private void AdjustLabelPosition(Label label, ProgressBar progressBar) { int newY = progressBar.Top - label.Height - 5; int newX = progressBar.Left + (progressBar.Width - label.Width) / 2; label.Location = new Point(newX, newY); } } }
关键修改点说明
- 文件大小统计:新增
totalValidBytes、unknownSizeFileCount和completedTotalBytes变量,分别统计有效总大小、未知大小文件数和已完成文件总大小,避免无效值干扰。 - 总进度条Maximum设置:分两种情况,有有效总大小时设为总字节数,无有效大小时设为文件总数,确保进度计算有意义。
- 总进度计算:根据是否有已知总大小,分别用字节数或文件完成比例计算进度,避免除以0的情况。
- 下载完成后更新:若文件大小预先获取失败,下载完成后用实际文件大小更新总进度数据,保证最终进度准确。
内容的提问来源于stack exchange,提问作者Shien
相关产品推荐
相关产品推荐

