web-dev-qa-db-ja.com

WebClientまたはHttpClientでファイルをダウンロードしますか?

URLからファイルをダウンロードしようとしていますが、WebClientとHttpClientを選択する必要があります。 this の記事とインターネット上の他のいくつかの記事を参照しました。どこでも、その優れた非同期サポートと他の.Net 4.5特権のために、HttpClientに行くことをお勧めします。しかし、私はまだ完全に納得しておらず、より多くのインプットが必要です。

私は以下のコードを使用してインターネットからファイルをダウンロードしています:

WebClient:

WebClient client = new WebClient();
client.DownloadFile(downloadUrl, filePath);

HttpClient:

using (HttpClient client = new HttpClient())
{        
    using (HttpResponseMessage response = await client.GetAsync(url))
    using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
    {
    }
}

私の観点から見ると、WebClientを使用する場合の唯一の欠点は、呼び出しスレッドをブロックする非非同期呼び出しです。しかし、スレッドのブロックについて心配していない場合、またはclient.DownloadFileAsync()を使用して非同期サポートを活用する場合はどうなりますか?

一方、HttpClientを使用する場合、ファイルのすべてのバイトをメモリにロードしてからローカルファイルに書き込むのではないでしょうか。ファイルサイズが大きすぎる場合、メモリのオーバーヘッドは高くなりませんか? WebClientを使用すると、ローカルファイルに直接書き込み、システムメモリを消費しないため、これは回避できます。

パフォーマンスが私の優先事項である場合、ダウンロードにはどのアプローチを使用する必要がありますか?上記の仮定が間違っている場合は明確にしたいと思います。また、別のアプローチも受け入れています。

18
Saket

これが私のアプローチです。

WebApiを呼び出してファイルを取得する場合、コントローラーメソッドからHttpClient GETリクエストを使用し、FileStreamResult戻り型を使用してファイルストリームを返すことができます。

public async Task<ActionResult> GetAttachment(int FileID)
{
    UriBuilder uriBuilder = new UriBuilder();
    uriBuilder.Scheme = "https";
    uriBuilder.Host = "api.example.com";

    var Path = "/files/download";
    uriBuilder.Path = Path;
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(uriBuilder.ToString());
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Add("authorization", access_token); //if any
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        HttpResponseMessage response = await client.GetAsync(uriBuilder.ToString());

            if (response.IsSuccessStatusCode)
            {
                System.Net.Http.HttpContent content = response.Content;
                var contentStream = await content.ReadAsStreamAsync(); // get the actual content stream
                return File(contentStream, content_type, filename);
            }
            else
            {
                throw new FileNotFoundException();
            }
    }
}
8
Ingale88s

これを使用してURLをダウンロードしてファイルに保存する1つの方法 :(Windows 7を使用しているため、WindowsRTを使用できないため、System.IOも使用しています。)

public static class WebUtils
{
    private static Lazy<IWebProxy> proxy = new Lazy<IWebProxy>(() => string.IsNullOrEmpty(Settings.Default.WebProxyAddress) ? null : new WebProxy { Address = new Uri(Settings.Default.WebProxyAddress), UseDefaultCredentials = true });

    public static IWebProxy Proxy
    {
        get { return WebUtils.proxy.Value; }
    }

    public static Task DownloadAsync(string requestUri, string filename)
    {
        if (requestUri == null)
            throw new ArgumentNullException(“requestUri”);

        return DownloadAsync(new Uri(requestUri), filename);
    }

    public static async Task DownloadAsync(Uri requestUri, string filename)
    {
        if (filename == null)
            throw new ArgumentNullException("filename");

        if (Proxy != null)
            WebRequest.DefaultWebProxy = Proxy;

        using (var httpClient = new HttpClient())
        {
            using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
            {
                using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.LargeBufferSize, true))
                {
                    await contentStream.CopyToAsync(stream);
                }
            }
        }
    }
} 

コードは、設定で(職場で)使用するプロキシサーバーのアドレスを保存し、そのような設定が指定されている場合はそれを使用することに注意してください。それ以外の場合は、HttpClientベータ版を使用してファイルをダウンロードおよび保存するために知っておく必要があるすべての情報が表示されます。

2
Nirzar

.Net 4.5+でネイティブに実行できます。私はそれをあなたのやり方でやってみましたが、それから理にかなっていると思われるIntellisenseのメソッドを見つけました。

https://docs.Microsoft.com/en-us/dotnet/api/system.io.stream.copytoasync?view=netframework-4.7.2

uri = new Uri(generatePdfsRetrieveUrl + pdfGuid + ".pdf");
HttpClient client = new HttpClient();
var response = await client.GetAsync(uri);
using (var fs = new FileStream(
    HostingEnvironment.MapPath(string.Format("~/Downloads/{0}.pdf", pdfGuid)), 
    FileMode.CreateNew))
{
    await response.Content.CopyToAsync(fs);
}
1
Bluebaron

繰り返し呼び出されるコードの場合、しないHttpClientusingブロックに入れたい(- それは吊りポートを開いたままにしておきます

HttpClientを使用してファイルをダウンロードする場合、 この拡張方法 が見つかりました。

public static class HttpContentExtensions
{
    public static Task ReadAsFileAsync(this HttpContent content, string filename, bool overwrite)
    {
        string pathname = Path.GetFullPath(filename);
        if (!overwrite && File.Exists(filename))
        {
            throw new InvalidOperationException(string.Format("File {0} already exists.", pathname));
        }

        FileStream fileStream = null;
        try
        {
            fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None);
            return content.CopyToAsync(fileStream).ContinueWith(
                (copyTask) =>
                {
                    fileStream.Close();
                });
        }
        catch
        {
            if (fileStream != null)
            {
                fileStream.Close();
            }

            throw;
        }
    }
}
0
Thymine