使用WebClient或HttpClient下载文件?

时间:2017-08-16 10:44:31

标签: c# .net rest httpclient webclient

我正在尝试从URL下载文件,我必须在WebClient和HttpClient之间进行选择。我在互联网上引用了this文章和其他几篇文章。无处不在,由于其强大的异步支持和其他.Net 4.5权限,建议使用HttpClient。但我仍然不完全相信并需要更多的投入。

我使用以下代码从互联网下载文件:

Web客户端:

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,可以避免这种情况,因为它会直接写入本地文件而不会占用系统内存。

那么,如果性能是我的首要任务,我应该使用哪种方法进行下载?如果我的上述假设是错误的,我想澄清一下,我也愿意接受替代方法。

7 个答案:

答案 0 :(得分:5)

这是我的方法。

如果要调用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 :(得分:2)

Here’s one way to use it to download a URL and save it to a file :(我正在使用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 beta下载和保存文件的所有信息。

答案 2 :(得分:0)

对于重复调用的代码,您不想function execAssetModule(code, path) { let script = new Script(code) let exports = {} let sandbox = { __webpack_public_path__: '', module: {exports}, exports, } script.runInNewContext(sandbox) return sandbox.module.exports } 放在HttpClient块(it will leave hanging ports open)中

对于使用HttpClient下载文件,我发现this extension method对于我来说似乎是一个很好且可靠的解决方案:

using

答案 3 :(得分:0)

您可以使用.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 :(得分:0)

如果您想要(或必须)同步执行此操作,但使用漂亮的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);
                }

答案 5 :(得分:0)

为了在使用 WebClient 的现有代码上使用 HttpClient,我编写了一个小的扩展方法来使用它,就像在我的代码中使用 DownloadFileTaskAsync 一样。

using (var client = new System.Net.Http.HttpClient()) // WebClient
{
    var fileName = @"C:\temp\imgd.jpg";
    var uri = new Uri("https://yourwebsite.com/assets/banners/Default.jpg");

    await client.DownloadFileTaskAsync(uri, fileName);
}

要使用它,我们可以使用这个扩展方法:

public static class HttpClientUtils
{
    public static async Task DownloadFileTaskAsync(this HttpClient client, Uri uri, string FileName)
    {
        using (var s = await client.GetStreamAsync(uri))
        {
            using (var fs = new FileStream(FileName, FileMode.CreateNew))
            {
                await s.CopyToAsync(fs);
            }
        }
    }
}

答案 6 :(得分:-1)

   HttpClient _client=new HttpClient();
   byte[] buffer = null;
   try
   {       
      HttpResponseMessage task = await _client.GetAsync("https://**FILE_URL**");
      Stream task2 = await task.Content.ReadAsStreamAsync();
      using (MemoryStream ms = new MemoryStream())
      {
        await task2.CopyToAsync(ms);
        buffer = ms.ToArray();
      }
      File.WriteAllBytes("C:/**PATH_TO_SAVE**", buffer);  
   }
   catch
   {

   }