私は、.net4.5の新しいAsyncおよびAwait機能の実例を見つけるためにどこでも探していました。ファイルのリストをダウンロードし、同時ダウンロードの数を制限するために、次のコードを考え出しました。このコードを改善/最適化するためのベストプラクティスまたは方法をいただければ幸いです。
次のステートメントを使用して、以下のコードを呼び出しています。
await this.asyncDownloadManager.DownloadFiles(this.applicationShellViewModel.StartupAudioFiles, this.applicationShellViewModel.SecurityCookie, securityCookieDomain).ConfigureAwait(false);
次に、イベントを使用して、ダウンロードしたファイルをViewModelのobservablecollection(.net 4.5の新しいスレッドセーフバージョン)に追加します。
public class AsyncDownloadManager
{
public event EventHandler<DownloadedEventArgs> FileDownloaded;
public async Task DownloadFiles(string[] fileIds, string securityCookieString, string securityCookieDomain)
{
List<Task> allTasks = new List<Task>();
//Limits Concurrent Downloads
SemaphoreSlim throttler = new SemaphoreSlim(initialCount: Properties.Settings.Default.maxConcurrentDownloads);
var urls = CreateUrls(fileIds);
foreach (var url in urls)
{
await throttler.WaitAsync();
allTasks.Add(Task.Run(async () =>
{
try
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
if (!string.IsNullOrEmpty(securityCookieString))
{
Cookie securityCookie;
securityCookie = new Cookie(FormsAuthentication.FormsCookieName, securityCookieString);
securityCookie.Domain = securityCookieDomain;
httpClientHandler.CookieContainer.Add(securityCookie);
}
await DownloadFile(url, httpClientHandler).ConfigureAwait(false);
}
finally
{
throttler.Release();
}
}));
}
await Task.WhenAll(allTasks).ConfigureAwait(false);
}
async Task DownloadFile(string url, HttpClientHandler clientHandler)
{
HttpClient client = new HttpClient(clientHandler);
DownloadedFile downloadedFile = new DownloadedFile();
try
{
HttpResponseMessage responseMessage = await client.GetAsync(url).ConfigureAwait(false);
var byteArray = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
if (responseMessage.Content.Headers.ContentDisposition != null)
{
downloadedFile.FileName = Path.Combine(Properties.Settings.Default.workingDirectory, responseMessage.Content.Headers.ContentDisposition.FileName);
}
else
{
return;
}
if (!Directory.Exists(Properties.Settings.Default.workingDirectory))
{
Directory.CreateDirectory(Properties.Settings.Default.workingDirectory);
}
using (FileStream filestream = new FileStream(downloadedFile.FileName, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await filestream.WriteAsync(byteArray, 0, byteArray.Length);
}
}
catch(Exception ex)
{
return;
}
OnFileDownloaded(downloadedFile);
}
private void OnFileDownloaded(DownloadedFile downloadedFile)
{
if (this.FileDownloaded != null)
{
this.FileDownloaded(this, new DownloadedEventArgs(downloadedFile));
}
}
public class DownloadedEventArgs : EventArgs
{
public DownloadedEventArgs(DownloadedFile downloadedFile)
{
DownloadedFile = downloadedFile;
}
public DownloadedFile DownloadedFile { get; set; }
}
Svickによる提案の後-以下は直接の質問です:
- 他のAsync/AwaitメソッドにAsync/Awaitを埋め込むとどのような影響がありますか?(Async/Awaitメソッド内でファイルストリームをディスクに書き込みます。
- httpclientを個別のタスクごとに使用する必要がありますか、それとも単一のタスクを共有する必要がありますか?
- イベントは、ダウンロードしたファイル参照をビューモデルに「送信」するための良い方法ですか?[コードレビューにも投稿します]