简体   繁体   English

DownloadFileTaskAsync 无法下载数千个文件

[英]DownloadFileTaskAsync does not work to download thousands of file

I have a thousands list of download url.我有数千个下载网址列表。 The url may contain a different type of file such as that image, pdf, audio, video etc. I am trying to download them using DownloadFileTaskAsync.该 url 可能包含不同类型的文件,例如该图像、pdf、音频、视频等。我正在尝试使用 DownloadFileTaskAsync 下载它们。 But after several files downloading the app is breaking down.但是在下载了几个文件后,该应用程序崩溃了。 I don't understand what is happening.我不明白发生了什么。 don't get any error message.不要收到任何错误信息。 Just my app is shut down after few files downloading.只是我的应用程序在下载几个文件后关闭。

foreach (var url in urls)
{
    //if file exists in our local directory then do not need to download and continue the process...
    if (FileExistsOrNot(localPath + "/" + url.fileName))
       continue;

    Thread thread = new Thread(async () => {
        WebClient client = new WebClient();
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
        client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
        await client.DownloadFileTaskAsync(new Uri(url.downloadUrl), localPath + "/" + url.fileName);  
    });
    thread.Start();
}

Spawning a new thread for every download it's no wonder that you quickly run out of system resources (threads in this case).为每次下载生成一个新线程,难怪您会很快耗尽系统资源(在本例中为线程)。

You should use some kind of thread pooling and luckily, C# has multiple out-of-the-box solution to do this:您应该使用某种线程池,幸运的是,C# 有多种开箱即用的解决方案来执行此操作:

ThreadPool is the lowest level construct, parallel programming and task schedulers are built on top of that. ThreadPool 是最低级别的构造,并行编程和任务调度程序构建在此之上。

As you use an async operation to download ( DownloadFileTaskAsync ), tasks are the most suitable option in this case:当您使用异步操作下载 ( DownloadFileTaskAsync ) 时,任务是这种情况下最合适的选项:

Action<string, DownloadProgressChangedEventArgs> onDownloadProgress = (url, e) => 
{ 
    /* your logic displaying progress... */ 
};

var downloadTasks = urls
    .Where(url => !FileExistsOrNot(Path.Combine(localPath, url.fileName)))
    .Select(async url =>
    {
        using (var client = new WebClient())
        {
            client.DownloadProgressChanged += (s, e) => onDownloadProgress(url.fileName, e);
            try { await client.DownloadFileTaskAsync(new Uri(url.downloadUrl), Path.Combine(localPath, url.fileName)); }
            catch (Exception ex) { /* handle download error: log exception, etc */ }                
        }
    });    

Task.WaitAll(downloadTasks.ToArray()); // or Task.WhenAll(...) if you want it non-blocking

Some remarks:一些备注:

  • use Path.Combine to build path strings to get correct results on every platform使用Path.Combine构建路径字符串以在每个平台上获得正确的结果
  • WebClient implements IDisposable , that is, it may use unmanaged resources. WebClient实现IDisposable ,也就是说,它可以使用非托管资源。 You should call Dispose on it as soon as you don't need it anymore, otherwise you can run out of system resources again.您应该在不再需要它时立即对其调用Dispose ,否则您可能会再次耗尽系统资源。
  • you don't need to observe DownloadFileCompleted .你不需要观察DownloadFileCompleted Awaiting DownloadFileTaskAsync rethrows potential errors as exceptions.等待DownloadFileTaskAsync会将潜在错误作为异常重新抛出。
  • consider using HttpClient instead of WebClient for better async support and performance考虑使用HttpClient而不是 WebClient 以获得更好的异步支持和性能

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

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