使用DownloadDataAsync等待多个文件下载

本文关键字:文件下载 等待 DownloadDataAsync 使用 | 更新日期: 2023-09-27 18:18:38

我有一个zip文件创建器,它接受url的String[],并返回一个包含String[]中所有文件的zip文件

我想会有一些这样的例子,但我似乎找不到一个答案"如何异步下载许多文件,并在完成时返回"

如何一次性下载{n}文件,并在所有下载完成后才返回Dictionary ?

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);
}

使用DownloadDataAsync等待多个文件下载

    首先,你用async-await标记了你的问题,而没有实际使用它。真的没有理由再使用旧的异步范例了。
  • 等待异步为所有并发async操作完成,你应该使用Task.WhenAll,这意味着你需要保持所有的任务在一些结构(即字典)之前实际提取他们的结果。
  • 最后,当您拥有所有结果时,只需通过解析uri到文件名来创建新的结果字典,并从async任务中提取结果。

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);
}
    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;
    }

你想要这个任务。WaitAll方法…

msdn联系

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

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

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;
            });
        }

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

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