C#使用BackgroundWorker下载文件时进度条不实时更新求助
问题:BackgroundWorker下载FTP文件时进度条不实时更新
我用BackgroundWorker从FTP服务器下载文件,但进度条仅在文件下载完成后才更新。下载过程中进度条停留在0状态并卡顿,下载完成后直接跳至100。以下是我的代码:
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { FtpWebRequest request = (FtpWebRequest)WebRequest.Create(activeDownload.ftpPath); request.Credentials = new NetworkCredential(ftpUser, ftpPass); request.Method = WebRequestMethods.Ftp.DownloadFile; FtpWebResponse response = (FtpWebResponse)request.GetResponse(); Stream responseStream = response.GetResponseStream(); using (FileStream writer = new FileStream(activeDownload.localPath, FileMode.Create)) { long length = response.ContentLength; int bufferSize = 2048; int readCount; byte[] buffer = new byte[2048]; long bytesread = 0; backgroundWorker1.ReportProgress(0); readCount = responseStream.Read(buffer, 0, bufferSize); while (readCount > 0) { bytesread += readCount; writer.Write(buffer, 0, readCount); backgroundWorker1.ReportProgress((int)(bytesread / activeDownload.fileSize) * 100); readCount = responseStream.Read(buffer, 0, bufferSize); } } response.Close(); responseStream.Close(); } private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; }
请问我哪里出现了错误?希望能得到各位的帮助。
问题分析与解决方案
嘿,我一眼就看到问题所在了——你在计算进度百分比的时候犯了一个整数除法的错误!
你看这句代码:
backgroundWorker1.ReportProgress((int)(bytesread / activeDownload.fileSize) * 100);
bytesread和activeDownload.fileSize都是长整型(long),当bytesread小于activeDownload.fileSize时,它们相除的结果是0(因为整数除法会直接舍弃小数部分),再乘以100还是0。只有当bytesread完全等于activeDownload.fileSize的时候,结果才会变成1*100=100,这就导致进度条一直卡在0,直到下载完成直接跳满。
另外还有个小细节:你已经用了using语句包裹FileStream来自动释放资源,但response和responseStream最好也用using来管理,这样能更安全地确保资源被正确释放,避免潜在的内存泄漏。
给你修正后的代码:
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { // 用sender获取当前的BackgroundWorker实例,比直接引用控件更灵活 BackgroundWorker worker = sender as BackgroundWorker; FtpWebRequest request = (FtpWebRequest)WebRequest.Create(activeDownload.ftpPath); request.Credentials = new NetworkCredential(ftpUser, ftpPass); request.Method = WebRequestMethods.Ftp.DownloadFile; // 给response和responseStream加上using,自动管理资源 using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) using (Stream responseStream = response.GetResponseStream()) using (FileStream writer = new FileStream(activeDownload.localPath, FileMode.Create)) { long totalBytes = response.ContentLength; int bufferSize = 2048; byte[] buffer = new byte[bufferSize]; long bytesRead = 0; int readCount; worker.ReportProgress(0); // 把readCount的赋值放到while条件里,代码更简洁 while ((readCount = responseStream.Read(buffer, 0, bufferSize)) > 0) { bytesRead += readCount; writer.Write(buffer, 0, readCount); // 修正百分比计算:先乘以100再做除法,避免整数除法丢失精度 int progress = (int)((bytesRead * 100) / totalBytes); worker.ReportProgress(progress); } } } private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; }
几个关键的修改点:
- 把百分比计算逻辑从
(bytesread / activeDownload.fileSize) * 100改成(bytesRead * 100) / totalBytes:先做乘法得到一个较大的数值,再做除法就能保留有效的百分比数值,避免了整数除法导致的精度丢失 - 用
sender获取当前的BackgroundWorker实例,而不是直接引用控件,这是更健壮的编程习惯 - 给
response和responseStream加上using语句,确保资源被正确释放 - 优化了while循环的写法,把
readCount的赋值放到条件里,让代码更简洁
这样修改后,进度条应该就能实时更新了,不会再卡顿在0啦!
内容的提问来源于stack exchange,提问作者Adam Stück
相关产品推荐
相关产品推荐

