C# 带进度条的异步文件下载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9459225/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 07:30:24  来源:igfitidea点击:

Asynchronous File Download with Progress Bar

c#asynchronousprogress-bar

提问by Josh Ferrara

I am attempting to have a progress bar's progress change as the WebClientdownload progress changes. This code still downloads the file yet when I call startDownload()the window freezes as it downloads the file. I would like for the user to be able to see the progress change as the splash screen loads. Is there any way to fix this so that the user can see the progress of progressBar2change?

随着WebClient下载进度的变化,我试图让进度条的进度发生变化。当我调用startDownload()窗口在下载文件时冻结时,此代码仍会下载文件。我希望用户能够在启动画面加载时看到进度变化。有没有办法解决这个问题,以便用户可以看到progressBar2更改的进度?

private void startDownload()
{
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
    client.DownloadFileAsync(new Uri("http://joshua-ferrara.com/luahelper/lua.syn"), @"C:\LUAHelper\Syntax Files\lua.syn");
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    double bytesIn = double.Parse(e.BytesReceived.ToString());
    double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
    double percentage = bytesIn / totalBytes * 100;
    label2.Text = "Downloaded " + e.BytesReceived + " of " + e.TotalBytesToReceive;
    progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    label2.Text = "Completed";
}

采纳答案by Bojin Li

You should call startDownload()from the UI thread. The whole idea of WebClient.DownloadFileAsync()is that it will spawn a worker thread for you automatically without blocking the calling thread. In startDownload(), you specified callbacks that modify controls which I assume were created by the UI thread. Thus if you call startDownload()from a background thread it will cause problems, because a thread can only modify UI elements it created.

您应该startDownload()从 UI 线程调用。整个想法WebClient.DownloadFileAsync()是它会自动为您生成一个工作线程,而不会阻塞调用线程。在 中startDownload(),您指定了修改控件的回调,我认为这些控件是由 UI 线程创建的。因此,如果您startDownload()从后台线程调用它会导致问题,因为线程只能修改它创建的 UI 元素。

The way it is supposed to work is you call startDownload()from the UI thread, startDownload()as you defined it sets up event call backs that are handled by the UI thread. It then starts the download asynchronously and returns immediately. The UI thread will be notified when the progress changes and the code responsible for updating the progress bar control will execute on the UI thread, and there shouldn't be any problems.

它应该工作的方式是startDownload()从 UI 线程调用,startDownload()正如您定义的那样,它设置由 UI 线程处理的事件回调。然后异步开始下载并立即返回。进度变化时会通知UI线程,负责更新进度条控件的代码会在UI线程上执行,应该不会有什么问题。

回答by Stephen Tetreault

I believe this article will lead you in the right direction http://www.dreamincode.net/forums/topic/115491-download-file-asynchronously-with-progressbar/.

我相信这篇文章会引导你走向正确的方向http://www.dreamincode.net/forums/topic/115491-download-file-asynchronously-with-progressbar/

And in this MSDN Article http://msdn.microsoft.com/en-us/library/ms229675.aspxdiscusses how "The file is downloaded on the BackgroundWorker component's worker thread, which runs the DoWork event handler. This thread starts when your code calls the RunWorkerAsync method."

在这篇 MSDN 文章http://msdn.microsoft.com/en-us/library/ms229675.aspx 中讨论了“文件是如何在运行 DoWork 事件处理程序的 BackgroundWorker 组件的工作线程上下载的。当你的代码调用 RunWorkerAsync 方法。”

回答by Peter PAD

UI Thread will be freezed when you click startDownload(). If you don't want get form freezed, you use startDownload()in another thread and make progress updating in cross-threaded. One way,

单击startDownload()时,UI 线程将被冻结。如果您不想让表单冻结,您可以在另一个线程中使用startDownload()并在跨线程中进行进度更新。单程,

private void startDownload()
{
    Thread thread = new Thread(() => {
          WebClient client = new WebClient();
          client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
          client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
          client.DownloadFileAsync(new Uri("http://joshua-ferrara.com/luahelper/lua.syn"), @"C:\LUAHelper\Syntax Files\lua.syn");
    });
    thread.Start();
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    this.BeginInvoke((MethodInvoker) delegate {
        double bytesIn = double.Parse(e.BytesReceived.ToString());
        double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
        double percentage = bytesIn / totalBytes * 100;
        label2.Text = "Downloaded " + e.BytesReceived + " of " + e.TotalBytesToReceive;
        progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
    });
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    this.BeginInvoke((MethodInvoker) delegate {
         label2.Text = "Completed";
    }); 
}

Read more multi-threading in Google like this http://msdn.microsoft.com/en-us/library/ms951089.aspx

像这样在 Google 中阅读更多多线程 http://msdn.microsoft.com/en-us/library/ms951089.aspx

-Fixed missing close ); to the bgThread declaration

- 修复丢失关闭);到 bgThread 声明

回答by guest

 public class ProgressEventArgsEx
{
    public int Percentage { get; set; }
    public string Text { get; set; }
}
public async static Task<string> DownloadStraingAsyncronous(string url, IProgress<ProgressEventArgsEx> progress)
{
    WebClient c = new WebClient();
    byte[] buffer = new byte[1024];
    var bytes = 0;
    var all = String.Empty;
    using (var stream = await c.OpenReadTaskAsync(url))
    {
        int total = -1;
        Int32.TryParse(c.ResponseHeaders[HttpRequestHeader.ContentLength], out total);
        for (; ; )
        {
            int len = await stream.ReadAsync(buffer, 0, buffer.Length);
            if (len == 0)
                break;
            string text = c.Encoding.GetString(buffer, 0, len);

            bytes += len;
            all += text;
            if (progress != null)
            {
                var args = new ProgressEventArgsEx();
                args.Percentage = (total <= 0 ? 0 : (100 * bytes) / total);
                progress.Report(args);
            }
        }
    }
    return all;
}
// Sample
private async void Bttn_Click(object sender, RoutedEventArgs e)
{
    //construct Progress<T>, passing ReportProgress as the Action<T> 
    var progressIndicator = new Progress<ProgressEventArgsEx>(ReportProgress);
    await TaskLoader.DownloadStraingAsyncronous(tbx.Text, progressIndicator);
}
private void ReportProgress(ProgressEventArgsEx args)
{
    this.statusText.Text = args.Text + " " + args.Percentage;
}