it-swarm.com.ru

Загрузить файл с помощью WebClient или HttpClient?

Я пытаюсь загрузить файл с URL-адреса, и мне приходится выбирать между WebClient и HttpClient. Я ссылался на это статью и несколько других статей в Интернете. Везде предлагается использовать HttpClient из-за его отличной асинхронной поддержки и других привилегий .Net 4.5. Но я все еще не полностью убежден, и мне нужно больше информации. 

Я использую приведенный ниже код для загрузки файла из интернета:

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, так как он будет напрямую записывать в локальный файл и не использовать системную память.

Итак, если производительность является моим главным приоритетом, какой подход я должен использовать для загрузки? Я хотел бы уточнить, если мое предположение неверно, и я также открыт для альтернативного подхода.

9
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();
            }
    }
}
1
Ingale88s

Вот один из способов использовать его для загрузки URL-адреса и сохранения его в файл: (Я использую 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 для загрузки и сохранения файла.

согласно следующему это ссылка:

1
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);
}
0
Bluebaron

Если код вызывается неоднократно, вы не не хотите помещать HttpClient в блок using ( он оставит открытые порты открытыми )

Для загрузки файла с помощью 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