Загрузить файл с помощью WebClient или HttpClient?
Я пытаюсь загрузить файл с URL-адреса, и мне нужно выбрать между WebClient и HttpClient. Я ссылался на эту статью и несколько других статей в Интернете. Всюду предлагается перейти на HttpClient из-за его большой поддержки async и других привилегий.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()
чтобы использовать поддержку async?
С другой стороны, если я использую HttpClient, я не загружаю каждый байт файла в память, а затем записываю его в локальный файл? Если размер файла слишком велик, не будет ли дорогостоящим издержки памяти? Который можно было бы избежать, если мы будем использовать WebClient, поскольку он будет напрямую записывать в локальный файл и не потреблять системную память.
Итак, если производительность - мой максимальный приоритет, какой подход я должен использовать для загрузки? Я хотел бы получить разъяснение, если мое предположение неверно, и я также открыт для альтернативного подхода.
Ответы
Ответ 1
Вот мой подход.
Если вы вызываете WebApi для получения файла, то из метода контроллера вы можете использовать запрос GETClient 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();
}
}
}
Ответ 2
Вот один из способов использовать его для загрузки 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 для загрузки и сохранения файла.
Ответ 3
Вы можете сделать это изначально с .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);
}
Ответ 4
Для многократного HttpClient
кода вы не хотите помещать 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;
}
}
}
Ответ 5
Если вы хотите (или должны) делать это синхронно, но с использованием классного класса HttpClient, тогда есть простой подход:
string requestString = @"https://example.com/path/file.pdf";
var GetTask = httpClient.GetAsync(requestString);
GetTask.Wait(WebCommsTimeout); // WebCommsTimeout is in milliseconds
if (!GetTask.Result.IsSuccessStatusCode)
{
// write an error
return;
}
using (var fs = new FileStream(@"c:\path\file.pdf", FileMode.CreateNew))
{
var ResponseTask = GetTask.Result.Content.CopyToAsync(fs);
ResponseTask.Wait(WebCommsTimeout);
}