使用进度条和状态标签下载异步

时间:2015-08-27 15:13:56

标签: c# .net winforms visual-studio asynchronous

在我的应用程序中,当您单击保存按钮时,它应该下载一个zip文件,同时显示进度,当下载完成时,它应该提取zip文件(使用DotNetZip lib)并继续进行。

当我测试它时,进度条会显示几秒钟的变化,然后冻结几秒钟并进入流程的最后阶段(提取已完成)。

到目前为止,这是我的代码:

    // Handle nginx save action
    private void saveNginX_Click(object sender, EventArgs e)
    {
        try
        {
            // Disable save button
            (sender as Button).Enabled = false;

            // Generate download & save file name
            var downloadLink = new Uri(myNginXConfig.selected_resource.DownloadLink);
            var saveFilename = @"apps\" + Path.GetFileName(downloadLink.AbsolutePath);

            // Check if file already exists
            if (File.Exists(saveFilename))
            {
                // Download completed, proceed to extracting
                NginXDownloadCompleted();
            }
            else
            {
                // Update status
                nginxStatus.Text = "Downloading " + selectNginX.SelectedItem.ToString() + " ...";
                Thread.Sleep(1000);

                // Init download
                using (WebClient webClient = new WebClient())
                {
                    webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(NginXDownloadProgressChangedEvent);
                    webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(NginXDownloadCompletedEvent);
                    webClient.DownloadFileAsync(downloadLink, saveFilename);
                }
            }
        }
        catch (Exception ex)
        {
            // Error
            Utils.ShowError("Failed to save - " + ex.Message);

            // Enable save button
            (sender as Button).Enabled = true;
        }
     }

    // Handle nginx download progress changed event
    private void NginXDownloadProgressChangedEvent(object sender, DownloadProgressChangedEventArgs e)
    {
        // Update the progressbar percentage only when the value is not the same.
        nginxProgress.Value = e.ProgressPercentage;

        // Calculate download progresss
        var downloadProgress = string.Format("{0} MB / {1} MB",
            (e.BytesReceived / 1024d / 1024d).ToString("0.00"),
            (e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00"));

        // Update download progress
        nginxStatus.Text = "Downloading " + downloadProgress + " ...";
    }

    // Handle nginx download complete event
    private void NginXDownloadCompletedEvent(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Cancelled != true)
            NginXDownloadCompleted();
    }

    // Handle nginx download complete
    private void NginXDownloadCompleted()
    {
        // Update status
        nginxStatus.Text = "Download completed; extracting ...";
        nginxStatus.Update();
        Thread.Sleep(1000);
        nginxProgress.Value = 0;

        // Generate download & save file name
        var downloadLink = new Uri(myNginXConfig.selected_resource.DownloadLink);
        var saveFilename = @"apps\" + Path.GetFileName(downloadLink.AbsolutePath);

        // Begin extracting the downloaded zip file
        using (ZipFile zip = ZipFile.Read(saveFilename))
        {
            zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(NginXExtractProgressEvent);
            zip.ExtractAll("apps", ExtractExistingFileAction.OverwriteSilently);
        }

        // Json encode setting and save it
        // File.WriteAllText(@"apps\NginXConfig.json", JsonConvert.SerializeObject(myNginXConfig, Formatting.Indented));
    }

    // Handle nginx extract progress event
    private void NginXExtractProgressEvent(object sender, ExtractProgressEventArgs e)
    {
        if (e.TotalBytesToTransfer > 0)
            nginxProgress.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer);

        if (e.EventType == ZipProgressEventType.Extracting_AfterExtractAll)
            NginXExtractCompleted();
    }

    // Handle nginx extract completed event
    private void NginXExtractCompleted()
    {
        // Update status
        nginxStatus.Text = "Extracting completed; generating config ...";
        nginxStatus.Update();
        Thread.Sleep(1000);
        nginxProgress.Value = 0;
    }

状态标签文本似乎也在做同样的事情。感觉/看起来好像过程不同步,它只是跳跃。

我尝试过使用Label.Update()方法,但这并没有帮助。

任何想法我可能做错了什么?

我希望在事件发生时以程序方式报告UI上状态更新的进度。

1 个答案:

答案 0 :(得分:0)

问题在于NginXDownloadCompleted方法。虽然DownloadFileAsync是异步方法,但ExtractAll不是,因此您的代码从Internet异步下载文件,但在文件提取时冻结主线程。

你需要的是 - 创建单独的后台线程(使用任何可用的选项:通过Thread类,通过ThreadPool,通过Tasks),并以同步的方式完成在此线程中下载和解压缩的全部工作。

一些注意事项:

  • 在主线程中你根本不需要Thread.Sleep它不会帮助
  • 不要忘记将新的单独线程中的异步下载替换为同步(DownloadFile而不是DownloadFileAsync)
  • 将在后台线程中触发的进度事件将在不同的上下文中工作,因此您无法直接更新UI,而是需要激活主线程来执行此操作(最简单的方法是使用Control.BeginInvoke)