【问题标题】:Multiple asynchronous download with progress bar带进度条的多个异步下载
【发布时间】:2016-09-17 21:42:13
【问题描述】:

我正在尝试从我的网络服务器下载文件夹,并且我想在进度条上显示已下载/要下载的数据总数的进度。首先我尝试使用WebClient.DownloadFile。哪个工作完美,但没有触发DownloadProgressChangedEventHandler。我猜它只能通过异步下载激活。所以我将我的方法改写为WebClient.DownloadFileAsync。这就是复杂的地方。

例如,我的 Web 服务器上有 30 个文件,大小为 53 MB。我想下载所有 30 个文件并在进度条上显示下载进度(并在其下显示 xx/53 MB 下载的标签)。

//Inicialized by opening dialog
private void DownloadForm_Shown(object sender, EventArgs e) {
   WebClient client = new WebClient();
   client.DownloadProgressChanged += client_DownloadProgressChanged;
   client.DownloadFileCompleted += client_DownloadFileCompleted;
   startDownload();
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
    progressBar.Value = e.ProgressPercentage;
    labelProgress.Text = String.Format("Downloaded {0} of {1} bytes", e.BytesReceived, e.TotalBytesToReceive);
}

private void startDownload() {
   //files contains all URL links
   foreach (string str in files) {
      string urlDownload = HttpUtility.UrlPathEncode(str);
      //location is variable where file will be stored
      client.DownloadFileAsync(new Uri(urlDownload), location);

      //without this, async download will go crazy and wont download anything
      while (client.IsBusy) { }
   }
}

我有这段代码,发生的事情是它会开始下载,但不会更新进度条,也不会更新标签。然后在下载后它将在大约 0.5 秒内更新进度和标签,仅此而已。我是这类事情的初学者,你能帮我找出错误吗?我知道如何为一个文件制作进度条。但是我必须做些什么来制作多个文件?

编辑:可以在此处找到带有一些解决方案的完整代码:http://pastebin.com/Hu4CCY8M 但是调用downloadURLs() 方法后UI 会冻结。完成此方法后,它将重新开始工作。

【问题讨论】:

    标签: c# .net asynchronous webclient


    【解决方案1】:

    如果没有可靠地重现问题的良好Minimal, Complete, and Verifiable code example,就不可能完全解决问题。也就是说,从您发布的代码中可以清楚地看出,这里的主要错误是您阻止了 UI 线程,从而阻止了在下载过程中对进度条的任何更改反映在用户界面上。

    “发疯”不是一个精确的问题描述,甚至接近于一个。也就是说,我希望尝试同时启动所有下载会导致WebClient 对象抛出异常,类似于“WebClient 不支持并发 I/O 操作”。如果您想要(也许您应该想要)同时下载文件,您需要拥有多个 WebClient 对象,每个并发下载一个。

    鉴于您明显打算一次下载一个文件,您需要在不阻塞 UI 线程的情况下这样做。您发布的代码甚至不可能是原始代码的精确复制/粘贴,因为您在 startDownload() 方法中使用标识符 client 而没有在任何地方声明它,也可能与本地代码相同DownloadForm_Shown() 方法中的变量 client。因此,暂时忽略这种差异,这是您发布的代码的变体,名义上可以解决您的问题:

    private TaskCompletionSource<bool> _tcs;
    
    private async void DownloadForm_Shown(object sender, EventArgs e) {
       WebClient client = new WebClient();
       client.DownloadProgressChanged += client_DownloadProgressChanged;
       client.DownloadFileCompleted += client_DownloadFileCompleted;
       await startDownload(client);
    }
    
    void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
        progressBar.Value = e.ProgressPercentage;
        labelProgress.Text = String.Format("Downloaded {0} of {1} bytes", e.BytesReceived, e.TotalBytesToReceive);
    }
    
    void client_DownloadFileCompleted(object sender, DownloadFileCompletedEventArgs e) {
        // whatever else you have in this event handler, and then...
        _tcs.SetResult(true);
    }
    
    private async Task startDownload(WebClient client) {
       //files contains all URL links
       foreach (string str in files) {
          string urlDownload = HttpUtility.UrlPathEncode(str);
          //location is variable where file will be stored
          _tcs = new TaskCompletionSource<bool>();
          client.DownloadFileAsync(new Uri(urlDownload), location);
          await _tcs.Task;
       }
       _tcs = null; 
    }
    

    这样,DownloadForm_Shown() 方法将在下载过程中将控制权交还给 UI 线程。在每个下载的文件完成后,将在startDownload() 方法中恢复执行,以便可以开始下一个。

    如果以上内容无法让您重回正轨,请改进问题,使其包含良好的 MCVE 和更准确的问题描述。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      • 1970-01-01
      • 2012-12-31
      • 2022-12-23
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多