【问题标题】:C# download file with progress indicator and then install fileC# 下载带有进度指示器的文件,然后安装文件
【发布时间】:2012-04-04 18:43:09
【问题描述】:

我正在尝试编写一个 C# Windows 应用程序来下载文件(等待下载完成 - 提供一些进度指示器),然后执行下载的文件。我想我要么需要:

  • 具有进度指示器且不会使我的应用程序无响应的同步下载
  • 等待下载完成然后尝试执行的异步下载。

前者(同步下载)似乎可以满足我的要求,但我找不到任何方式来指示进度,而且它似乎使我的程序无响应(就像它挂起一样)——这可能会导致用户关闭应用程序,而不是等待它完成下载。

后来(异步下载)我已经能够使用进度指示器等来完成,但发生的是下载开始并且我的应用程序在完成下载之前立即尝试安装它,这当然会失败。

那么实现这一点的最佳方法是什么?下面是我目前使用进度条进行异步下载的代码。

2012 年 4 月 10 日编辑

旧代码处理起来很麻烦,所以这就是我目前拥有的。它可以工作,只是它不更新进度条。

    private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {

            List<App> FilesToDownload = e.Argument as List<App>;

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                //string Text = ;
                UpdateRichTextBox("Downloading " + Program.Filename + "... Please wait");

                string Source = Program.DownloadLocation + "/" +  Program.Filename;
                string Destination = System.AppDomain.CurrentDomain.BaseDirectory + Program.Filename;

                WebClient webClient = new WebClient();
                webClient.DownloadFile(Source, @Destination);
            }

            UpdateRichTextBox("File download complete");

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                string Filename = Program.Filename;
                string Arguments = Program.InstallParameters;

                if (Filename != "advisorinstaller.exe")
                {
                    Process p = new Process();
                    p.StartInfo.FileName = System.AppDomain.CurrentDomain.BaseDirectory + Filename;
                    p.StartInfo.Arguments = Arguments;
                    p.Start();
                    p.WaitForExit();
                }
                else
                {
                    MessageBox.Show("About to install Belarc Advisor. This may take 10-15 minutes to run - do not shutdown this program. Click OK when ready to proceed.");
                }
            }
    }

    private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        UpdateRichTextBox("Install Complete");

    }

2012 年 4 月 11 日编辑

所以我编辑了我的后台工作人员的工作如下:

        private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {

            List<App> FilesToDownload = e.Argument as List<App>;

            int counter = 0;
            int percent = 0;

            foreach (App Program in FilesToDownload) // Loop through List with foreach
            {
                percent = ((counter / FilesToDownload.Count) * 100);
                backgroundWorker1.ReportProgress(percent, "");

                UpdateRichTextBox("Downloading " + Program.Filename + "... Please wait");

                string Source = Program.DownloadLocation + "/" +  Program.Filename;
                string Destination = System.AppDomain.CurrentDomain.BaseDirectory + Program.Filename;

                WebClient webClient = new WebClient();
                webClient.DownloadFile(Source, @Destination);


                counter++;

            }

            //backgroundWorker1.ReportProgress(100, "Complete!");
    }

如果我取消最后一行的注释,进度条会变为 100%。但它没有使用:

percent = ((counter / FilesToDownload.Count) * 100);
backgroundWorker1.ReportProgress(percent, "");

有什么想法吗?

【问题讨论】:

  • 执行的代码在哪里?会认为这将在 client_DownloadFileCompleted 处理程序中处理。
  • 用户按下一个按钮来检查某些复选框是否被选中,然后调用方法进行安装。安装方法依次调用下载文件方法,然后尝试安装下载的文件。我看看能不能发个例子。
  • 嗯,那你为什么要在 Completed 处理程序之外做 Process 的东西呢?似乎那将是完成工作或启动另一个线程来完成工作的地方。您之前提到过尝试在下载失败之前执行(当然),那么为什么不直接将该执行放入指示下载完成的方法中呢?

标签: c# asynchronous download


【解决方案1】:

您可以使用BackgroundWorker 在后台执行同步下载。它也支持进度报告。这将避免阻塞您的 UI(因为下载在不同的线程上运行)。

【讨论】:

  • 我正在尝试这样做,但运气不佳。我添加了一个后台工作人员。并创建了以下 DoWork、ProgressChanged 和 RunWorker Completed 方法。但是,如何将源和目标传递给 DoWork 方法?我正在下载几个文件,具体取决于用户在应用程序中所做的更改,因此我无法对它们进行硬编码。我需要将它们作为参数传递。
  • Chris - 这似乎使我的应用程序不再“锁定”,但它也没有更新我的进度条。关于我如何解决这个问题的任何建议?我在上面发布了我的更新代码。
  • @Brad:不确定这是否只是复制粘贴错误,但您发布的代码未订阅ProgressChanged 事件处理程序。
  • 我有一个名为 btnBegin_Click_1 的方法,它调用后台工作人员,我认为应该更改更新进度吗?这是我的代码: backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; backgroundWorker1.RunWorkerAsync(FilesToDownload);
【解决方案2】:

您可以使用ManualResetEvent 类在您的主线程上等待(使用WaitOne()),直到调用您的client_DownloadFileCompleted() 事件处理程序(在您的对象上调用Set())。

【讨论】:

    【解决方案3】:

    您是否考虑过使用 ClickOnce 来执行此操作?它将与自动更新一起解决您的问题。

    查看此 SO 问题以了解开始的地方:https://stackoverflow.com/questions/1635103/how-to-basic-tutorial-to-write-a-clickonce-app

    【讨论】:

    • 我不打算部署任何更新 - 这是一次性安装。此外,该应用程序不仅仅是安装(它复制配置文件等)。所以我不确定这是一个好的解决方案
    【解决方案4】:

    无需将WebClient 包装在BackgroundWorker 中,因为它已经是异步的(使用线程池中的线程)。

    注意:下面的示例是在编辑器中编写的,而我检查了 MSDN 文档中的方法签名,我很容易出错。

    public void InstallSecuniaPSI()
    {
        var source = new Uri("http://www.webserver.com:8014/psisetup.exe");
        var dest = Path.Combine(Path.GetTemp(), "Download_SecuniaPSI.exe");
    
        var client = new WebClient();
        client.DownloadProgressChanged += OnDownloadProgressChanged;
        client.DownloadFileCompleted += OnDownloadComplete;
        client.DownloadFileAsync(source, dest, dest);   
    }
    
    private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        // report progress
        Console.WriteLine("'{0}' downloaded {1} of {2} bytes. {3}% complete"(string)e.UserState, e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage);
    }
    
    private void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
    {
        // clean up
        var client = (WebClient)sender;
        client.DownloadProgressChanged -= OnDownloadProgressChanged;
        client.DownloadFileCompleted -= OnDownloadComplete;
    
        if (e.Error != null)
            throw e.Error;
    
        if (e.Cancelled)
            Environment.Exit(1);
    
        // install program
        var downloadedFile = (string)e.UserState;
        var processInfo = new ProcessStartInfo(downloadedFile, "/S");
        processInfo.CreateNoWindow = true;
    
        var installProcess = Process.Start(processInfo);
        installProcess.WaitForExit();
    
        Environment.Exit(installProcess.ExitCode);
    }
    

    现在唯一需要排序的是如何让程序等待;如果是控制台程序,则在InstallSecuniaPSI() 之后调用Console.ReadKey()

    HTH,

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-12
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      • 1970-01-01
      相关资源
      最近更新 更多