使用WebClient或HttpClient下载文件?

Sak*_*ket 19 .net c# rest webclient httpclient

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

我使用下面的代码从互联网上下载文件:

Web客户端:

WebClient client = new WebClient();
client.DownloadFile(downloadUrl, filePath);
Run Code Online (Sandbox Code Playgroud)

HttpClient的:

using (HttpClient client = new HttpClient())
{        
    using (HttpResponseMessage response = await client.GetAsync(url))
    using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

从我的角度来看,我只能看到使用WebClient的一个缺点,那就是非异步调用,阻塞调用线程.但是,如果我不担心阻塞线程或使用client.DownloadFileAsync()异步支持,该怎么办?

另一方面,如果我使用HttpClient,是不是我将文件的每个字节加载到内存中然后将其写入本地文件?如果文件太大,内存开销不会很贵吗?如果我们使用WebClient可以避免这种情况,因为它将直接写入本地文件而不会消耗系统内存.

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

Blu*_*ron 31

您可以使用 .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);
}
Run Code Online (Sandbox Code Playgroud)

  • @Bluebaron 不,你必须使用 `using (xxx) { ... }` 或 `using var xxx = ...;` 在作用域结束后处理它。 (3认同)

Ton*_*ony 29

为了在使用 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


San*_*ale 8

这是我的方法。

如果要调用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();
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你不应该使用 HttpClient 作为 Disposable 对象,尽管它是,如果你有很多请求,你会得到套接字耗尽。使用 httpClient 有一个静态实例,网上有很多文章讨论了这个问题。 (9认同)

Thy*_*ine 6

对于重复调用的代码,您不想放入块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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这里\xe2\x80\x99s是使用它下载URL并将其保存到文件的一种方法:(我使用的是Windows 7,因此我没有可用的WindowsRT,所以我\xe2\x80\x99m也使用System.IO .)

\n\n
public static class WebUtils\n{\n    private static Lazy<IWebProxy> proxy = new Lazy<IWebProxy>(() => string.IsNullOrEmpty(Settings.Default.WebProxyAddress) ? null : new WebProxy { Address = new Uri(Settings.Default.WebProxyAddress), UseDefaultCredentials = true });\n\n    public static IWebProxy Proxy\n    {\n        get { return WebUtils.proxy.Value; }\n    }\n\n    public static Task DownloadAsync(string requestUri, string filename)\n    {\n        if (requestUri == null)\n            throw new ArgumentNullException(\xe2\x80\x9crequestUri\xe2\x80\x9d);\n\n        return DownloadAsync(new Uri(requestUri), filename);\n    }\n\n    public static async Task DownloadAsync(Uri requestUri, string filename)\n    {\n        if (filename == null)\n            throw new ArgumentNullException("filename");\n\n        if (Proxy != null)\n            WebRequest.DefaultWebProxy = Proxy;\n\n        using (var httpClient = new HttpClient())\n        {\n            using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))\n            {\n                using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.LargeBufferSize, true))\n                {\n                    await contentStream.CopyToAsync(stream);\n                }\n            }\n        }\n    }\n} \n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,代码将我(在工作中)使用的代理服务器的地址保存在设置中,并在指定了此类设置的情况下使用该地址。否则,它应该告诉您有关使用 HttpClient beta 下载和保存文件所需了解的所有信息。

\n

  • 大部分代码与问题无关。HttpClient 实例*不应该*立即被处置。 (4认同)
  • https://codereview.stackexchange.com/questions/69950/single-instance-of-reusable-httpclient可能值得一读。 (2认同)