使用DownloadDataAsync下载AWAIT多个文件

时间:2014-08-18 20:45:50

标签: c# .net async-await webclient webclient-download

我有一个zip文件创建者,它接收String[]个Urls,并返回一个包含String[]

中所有文件的zip文件

我认为会有很多这方面的例子,但我似乎无法找到答案"如何异步下载多个文件并在完成后返回"

如何一次下载{n}个文件,并且仅在所有下载完成后返回词典?

private static Dictionary<string, byte[]> ReturnedFileData(IEnumerable<string> urlList)
{
    var returnList = new Dictionary<string, byte[]>();
    using (var client = new WebClient())
    {
        foreach (var url in urlList)
        {
            client.DownloadDataCompleted += (sender1, e1) => returnList.Add(GetFileNameFromUrlString(url), e1.Result);
            client.DownloadDataAsync(new Uri(url));
        }
    }
    return returnList;
}

private static string GetFileNameFromUrlString(string url)
{
    var uri = new Uri(url);
    return System.IO.Path.GetFileName(uri.LocalPath);
}

3 个答案:

答案 0 :(得分:5)

  • 首先,您使用async-await标记了您的问题,但未实际使用它。没有理由再使用旧的异步范例了。
  • 要等待异步以完成所有并发async操作,您应该使用Task.WhenAll,这意味着您需要在某些构造(即字典)中保留所有任务实际上提取他们的结果。
  • 最后,当您掌握了所有结果时,您只需将uri解析为文件名,然后从async任务中提取结果,即可创建新的结果字典。 / LI>

async Task<Dictionary<string, byte[]>> ReturnFileData(IEnumerable<string> urls)
{
    var dictionary = urls.ToDictionary(
        url => new Uri(url),
        url => new WebClient().DownloadDataTaskAsync(url));

    await Task.WhenAll(dictionary.Values);

    return dictionary.ToDictionary(
        pair => Path.GetFileName(pair.Key.LocalPath),
        pair => pair.Value.Result);
}

答案 1 :(得分:0)

    public string JUST_return_dataURL_by_URL(string URL, int interval, int max_interval)
    {
        var client = new WebClient(proxy);
        client.Headers = _headers;
        string downloaded_from_URL = "false";       //default - until downloading
        client.DownloadDataCompleted += bytes => 
        {
            Console.WriteLine("Done!");
            string dataURL = Convert.ToBase64String( bytes );
            string filename = Guid.NewGuid().ToString().Trim('{', '}')+".png";
            downloaded_from_URL =
                        "Image Downloaded from " + URL
                    +   "<br>"
                    +   "<a href=\""+dataURL+"\" download=\""+filename+"\">"
                    +       "<img src=\"data:image/png;base64," + dataURL + "\"/>"+filename
                    +   "</a>"
            ;
            return;
        };
        client.DownloadDataAsync(new System.Uri(URL));

        int i = 0;
        do{
        //  Console.WriteLine(
        //      "(interval > 10): "+(interval > 10)
        //      +"\n(downloaded_from_URL == \"false\"): " + (downloaded_from_URL == "false")
        //      +"\ninterval: "+interval
        //  );
            Thread.Sleep(interval);
            i+=interval;
        }
        while( (downloaded_from_URL == "false") && (i < max_interval) );

        return downloaded_from_URL;
    }

答案 2 :(得分:-1)

你想要这个任务。等待所有方法......

msdn link

将每个下载创建为单独的任务,然后将它们作为集合传递。

此快捷方式可能是将下载方法包装在任务中。

Return new Task<downloadresult>(()=>{ method body});

对于模糊的道歉,在iPad上工作很难编码。

修改

可能值得考虑的另一个实现是使用并行框架包装下载。

由于您的任务都采用相同的方式获取参数,因此您可以使用Parallel.Foreach并将其包装到单个任务中:

public System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, byte[]>> DownloadTask(System.Collections.Generic.IEnumerable<string> urlList)
        {
            return new System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, byte[]>>(() =>
            {
                var r = new System.Collections.Concurrent.ConcurrentDictionary<string, byte[]>();
                System.Threading.Tasks.Parallel.ForEach<string>(urlList, (url, s, l) =>
                {
                    using (System.Net.WebClient client = new System.Net.WebClient())
                    {
                        var bytedata = client.DownloadData(url);
                        r.TryAdd(url, bytedata);
                    }
                });


                var results = new System.Collections.Generic.Dictionary<string, byte[]>();
                foreach (var value in r)
                {
                    results.Add(value.Key, value.Value);
                }

                return results;
            });
        }

在转换回IDictionary之前,它利用并发集合来支持方法中的并行访问。

此方法返回一个任务,因此可以使用await调用。

希望这提供了一个有用的选择。