简体   繁体   English

使用c#并行下载多个文件

[英]Download multiple files in parallel using c#

I want to download files in parallel using C#.我想使用 C# 并行下载文件。 For this, I have written this code which is working perfectly but the problem is that UI is freezing.为此,我编写了这段运行良好的代码,但问题是 UI 冻结了。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace FileDownloader
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private static int count = 1;
        private static string f= "lecture";
        private string URL = "www.someexample.com";

        public MainWindow()
        {
            InitializeComponent();
        }

        public static string GetDirectoryListingRegexForUrl(string url)
        {
            if (url.Equals(URL))
            {
                return "<a href=\".*\">(?<name>.*)</a>";
            }
            throw new NotSupportedException();
        }

        public  void DownloadP(string[] urls)
        {
            Parallel.ForEach(urls.ToList(), new ParallelOptions { MaxDegreeOfParallelism = 10 }, DownloadFile);
        }

        private void DownloadFile(string url)
        {
           using(WebClient client=new WebClient())
           {
               if (url.EndsWith(".pdf"))
               {
                   int nextIndex = Interlocked.Increment(ref count);

                   client.DownloadFile(url, f + nextIndex + ".pdf");
                   this.Dispatcher.Invoke(() => {
                       listbox.Items.Add(url);
                   });
               }
           }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DownloadP(listofFiles);
        }
    }
}

You can use async/await with conjunction with new WebClient method DownloadFileTaskAsync . 您可以将async/await与新的WebClient方法DownloadFileTaskAsync结合使用。

private async Task DownloadFile(string url)
{
    if (!url.EndsWith(".pdf"))
    {
        return;
    }

    using (var client = new WebClient())
    {
        int nextIndex = Interlocked.Increment(ref count);

        await client.DownloadFileTaskAsync(url, "lecture" + nextIndex + ".pdf");
        listBox.Items.Add(url);

    }
}

private async void Button_OnClick(object sender, RoutedEventArgs e)
{
    button.IsEnabled = false;
    await DownloadFiles(urlList);
    button.IsEnabled = true;
}

private async Task DownloadFiles(IEnumerable<string> urlList)
{
    foreach (var url in urlList)
    {
        await DownloadFile(url);
    }
}

instead of using client.DownloadFile use client.DownloadFileAsync like this 而不是使用client.DownloadFile使用client.DownloadFileAsync这样

var webClient=new WebClient();
webClient.DownloadFileCompleted += webClient_DownloadFileCompleted;
webClient.DownloadFileAsync("Your url","file_name");

the event 事件

    private void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        MessageBox.Show("Download Completed.");
    }

I know this Question is old, but WebClient is outdated and dedicated.我知道这个问题很旧,但是 WebClient 已经过时且专用。 First of all, WPF uses MVVM pattern that you should try to follow, but except that you can use the HttpClient in using System.Net.Http;首先,WPF 使用您应该尝试遵循的 MVVM 模式,但除了您可以在using System.Net.Http; . .

To download multiple files in a parallel you can create a parallel foreach that handles all the downloads that the HttpClient have to perform.要并行下载多个文件,您可以创建一个并行的 foreach 来处理 HttpClient 必须执行的所有下载。 You did tis right but the foreach will block the thread so start it in a new one Task.Run(()=>{// Your Code});你做对了,但是 foreach 会阻塞线程,所以在一个新的Task.Run(()=>{// Your Code});

In the case you don't want do write something like that yourself, you could use the Shard Download Library on NuGet.如果您不想自己编写类似的东西,您可以使用 NuGet 上的碎片下载库 This NuGet Package is also on GitHub if you want to see how it does its job.这个 NuGet 包也在 GitHub 上,如果你想看看它是如何工作的。 It helped me often if I want do download a lot of files, because is does not block the UI thread and is easy to use.如果我想下载很多文件,它经常帮助我,因为它不会阻塞 UI 线程并且易于使用。 To use it you have to write something like this:要使用它你必须写这样的东西:

void Download()
{
    string[] links = new string[]{ 
        "https://google.com",
        "https://speed.hetzner.de/100MB.bin",
        "https://file-examples.com/storage/fe88dacf086398d1c98749c/2017/04/file_example_MP4_1920_18MG.mp4" };
    foreach (var link in links)
    {
        _ = new LoadRequest(link);
    }
    Console.ReadLine();
}

But you can also set the MaxDegreeOfParalism and you can change it while it is downloading.但是您也可以设置 MaxDegreeOfParalism 并且可以在下载时更改它。 You can set the Path for the output file and name it.您可以设置输出文件的路径并为其命名。 The problem that the HttpClient has with download finished and progress report is also solved with this. HttpClient 的下载完成和进度报告的问题也由此解决。 The bad thing is that the documentation of this library is not very good.不好的是这个库的文档不是很好。 Here an example with few options as an example.这里以几个选项为例。

async Task DownloadAsync()
{
    string[] links = new string[]{
        "https://google.com",
        "https://speed.hetzner.de/100MB.bin",
        "https://file-examples.com/storage/fe88dacf086398d1c98749c/2017/04/file_example_MP4_1920_18MG.mp4" };
    RequestHandler requestHandler = new()
    {
        MaxDegreeOfParallelism = 10
    };

    LoadRequestOptions option = new()
    {
        Progress = new Progress<float>(value => Console.WriteLine(value.ToString("0.0%"))),
        DestinationPath = "C:\\Users\\[UserName]\\Desktop\\",
        RequestCompleated = path => Console.WriteLine("Finished: " + path?.ToString()),
        RequestHandler = requestHandler
    };
    LoadRequest last = null;
    foreach (string link in links)
    {
        last = new LoadRequest(link, option);
    }
    await last.Task;
} 

I hope that can help people that have the same problem and don't want to use the dedicated WebClient.我希望这可以帮助那些有同样问题并且不想使用专用 WebClient 的人。

Relace your DownloadP function with this : 用以下方法重新启动DownloadP功能:

public async Task DownloadP(string[] urls)
{
  await Task.Factory.StartNew(() => Parallel.ForEach(urls.ToList(), new ParallelOptions { MaxDegreeOfParallelism = 10 }, DownloadFile));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM