异步FTP文件下载及进度条显示的技术实现问题
解决FTP下载进度条跨线程更新问题
看起来你现在的代码存在UI线程跨线程访问的风险——直接在Task.Run的后台线程里操作pBar这个UI控件,会触发InvalidOperationException,因为WPF/WinForms的UI控件只能在创建它们的线程(也就是UI线程)上修改。
我给你调整一下代码,用.NET推荐的IProgress<T>接口来安全更新进度条,既简洁又符合最佳实践:
第一步:修改自定义FtpClient的DownloadFile方法
不要直接传递ProgressBar控件,而是接收IProgress<int>来报告进度百分比:
public string DownloadFile(string source, string destination, IProgress<int> progress) { // 初始化FTP请求(替换成你自己的FTP连接逻辑) FtpWebRequest request = (FtpWebRequest)WebRequest.Create($"ftp://your-ftp-server/{source}"); request.Method = WebRequestMethods.Ftp.DownloadFile; // 添加FTP认证信息,比如 request.Credentials = new NetworkCredential("用户名", "密码"); using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()) using (Stream responseStream = response.GetResponseStream()) using (FileStream fileStream = new FileStream(destination, FileMode.Create)) { long totalFileSize = response.ContentLength; long downloadedBytes = 0; byte[] buffer = new byte[4096]; // 4KB分块下载,可根据需求调整大小 int bytesRead; while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) { fileStream.Write(buffer, 0, bytesRead); downloadedBytes += bytesRead; // 计算当前进度百分比并报告 int progressPercent = (int)((downloadedBytes * 100) / totalFileSize); progress?.Report(progressPercent); } return $"Done: {Path.GetFileName(source)} 下载完成"; } }
第二步:修改调用逻辑,安全更新UI
在UI线程创建Progress<int>对象,它会自动在UI线程处理进度更新,不用手动写Dispatcher:
SaveFileDialog sfd = new SaveFileDialog(); if (sfd.ShowDialog() == true) { pBar.Visibility = Visibility.Visible; pBar.Minimum = 0; pBar.Maximum = 100; pBar.Value = 0; // 重置进度条初始状态 string filename = entry.FileName.Text; // 创建进度回调,自动在UI线程执行更新 var downloadProgress = new Progress<int>(percent => { pBar.Value = percent; }); // 后台执行下载任务,不阻塞UI string status = await Task.Run(() => { return client.DownloadFile(filename, sfd.FileName, downloadProgress); }); statusBox.Text = status.Substring(4); }
关键要点解释
- 避免直接操作UI控件:后台线程永远不要直接修改UI元素,
IProgress<T>是.NET官方推荐的跨线程UI更新方案,内部已经封装了Dispatcher的调用逻辑,不用自己写繁琐的线程切换代码。 - 分块下载+进度计算:用固定大小的缓冲区分块读取文件,实时计算已下载字节占总大小的比例,保证进度条平滑更新,不会出现跳变。
- 异步等待:用
await Task.Run()避免阻塞UI线程,让界面在下载过程中保持可响应状态,不会出现假死。
内容的提问来源于stack exchange,提问作者IAmVisco
相关产品推荐
相关产品推荐

