【问题标题】:Wait for download to complete等待下载完成
【发布时间】:2025-12-08 23:30:01
【问题描述】:

我正在尝试创建一个简单的程序来下载几个文件。我尝试了一些在网上找到的现成解决方案,但我无法让它按照我想要的方式工作。我正在使用这个:

    private void startDownload(string toDownload, string saveLocation)
    {
        Thread thread = new Thread(() =>
        {
            WebClient client = new WebClient();
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
            client.DownloadFileAsync(new Uri(toDownload), saveLocation);
        });
        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;
            labelPercentage.Text = "Downloading " + Convert.ToInt32(percentage) + "%  -  " + Convert.ToInt32(bytesIn / 1024) + " / " + Convert.ToInt32(totalBytes / 1024) + " kB";
            progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
        });
    }

    void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        this.BeginInvoke((MethodInvoker)delegate
        {
            textBoxLog.AppendText("OK");
        });
    }

我想让程序在下载完成后继续运行(下载下一个文件/显示“OK”消息/执行下一行代码中的任何内容)。 在当前的形式中,如果我输入例如。

    private void button1_Click(object sender, EventArgs e)
    {
        startDownload(url, localpath + @"\file.zip");
        textBoxLog.AppendText("the cake is a lie");
    }

它首先向我显示此文本,然后显示“OK”。

我是从 c#/.net 开始的,而且我以前从未学习过面向对象的编程,所以这对我来说是一种双重挑战,我自己也搞不清楚。如果能提供相对简单的解释,我将不胜感激。

【问题讨论】:

  • 我不明白这个问题。下载完成后你已经了。你究竟不能做什么?你尝试了什么?你被困在哪里了?
  • 我将下载大约 50 个文件,这些文件将循环完成。 foreach(文件中的字符串文件) startDownload(...) 将同时下载 50 个文件,进度条将得到帕金森病,并且程序将继续执行在所有文件下载之前不应执行的操作。
  • 使用 thread.Join // 等到线程完成)大线程:*.com/questions/1584062/…

标签: c# download .net-4.5


【解决方案1】:

你可以通过Application.DoEvents()让startDownload等待异步文件下载,如下:

private bool downloadComplete = false;

private void startDownload(Uri toDownload, string saveLocation)
{
    string outputFile = Path.Combine(saveLocation, Path.GetFileName(toDownload.AbsolutePath));

    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
    client.DownloadFileAsync(toDownload, outputFile);

    while (!downloadComplete)
    {
        Application.DoEvents();
    }

    downloadComplete = false;
}

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// No changes in this method...
}

void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    this.BeginInvoke((MethodInvoker)delegate
    {
        textBoxLog.AppendText("OK");
        downloadComplete = true;
    });
}

还有下载队列...

private void button1_Click(object sender, EventArgs e)
{
    FireDownloadQueue(urls, localpath);
    textBoxLog.AppendText("the cake is a lie");
}

private async void FireDownloadQueue(Uri[] toDownload, string saveLocation)
{
    foreach (var url in urls)
    {
        await Task.Run(() => startDownload(url, localpath));
    }
}

但是,我认为您最好阅读有关 HttpWebRequest 并编写自己的下载器类并进行适当的检查和事件... 这是 Hemanshu Bhojak (Source) 的一个很好的例子,您可以扩展:

public class Downloader
{
    public async Task Download(string url, string saveAs)
    {
        var httpClient = new HttpClient();
        var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
        var parallelDownloadSuported = response.Headers.AcceptRanges.Contains("bytes");
        var contentLength = response.Content.Headers.ContentLength ?? 0;

        if (parallelDownloadSuported)
        {
            const double numberOfParts = 5.0;
            var tasks = new List<Task>();
            var partSize = (long)Math.Ceiling(contentLength / numberOfParts);

            File.Create(saveAs).Dispose();

            for (var i = 0; i < numberOfParts; i++)
            {
                var start = i*partSize + Math.Min(1, i);
                var end = Math.Min((i + 1)*partSize, contentLength);

                tasks.Add(
                    Task.Run(() => DownloadPart(url, saveAs, start, end))
                    );
            }

            await Task.WhenAll(tasks);
        }
    }

    private async void DownloadPart(string url, string saveAs, long start, long end)
    {
        using (var httpClient = new HttpClient())
        using (var fileStream = new FileStream(saveAs, FileMode.Open, FileAccess.Write, FileShare.Write))
        {
            var message = new HttpRequestMessage(HttpMethod.Get, url);
            message.Headers.Add("Range", string.Format("bytes={0}-{1}", start, end));

            fileStream.Position = start;
            await httpClient.SendAsync(message).Result.Content.CopyToAsync(fileStream);
        }
    }
}

示例用法:

Task.Run(() => new Downloader().Download(downloadString, saveToString)).Wait();

大致如下:

public class Downloader
{
    public event EventHandler DownloadProgress;
    DownloaderEventArgs downloaderEventArgs;

    public void DownloadStarted(DownloaderEventArgs e)
    {
        EventHandler downloadProgress = DownloadProgress;
        if (downloadProgress != null)
            downloadProgress(this, e);
    }

    // ...
}

class DownloaderEventArgs : EventArgs
{
    public string Filename { get; private set; }
    public int Progress { get; private set; }

    public DownloaderEventArgs(int progress, string filename)
    {
        Progress = progress;
        Filename = filename;
    }
    public DownloaderEventArgs(int progress) : this(progress, String.Empty)
    {
        Progress = progress;
    }
}

【讨论】:

  • 点赞“Application.DoEvents();”这一行。这就是我缺少的拼图。
【解决方案2】:

startDownload 在新线程上启动下载,因此当您调用startDownload 时,它会启动线程,之后的其余代码会立即继续,因为它位于单独的线程上。这就是为什么您在“OK”之前看到“蛋糕是谎言”的原因。

【讨论】:

    最近更新 更多