web-dev-qa-db-fra.com

Télécharger le fichier avec WebClient ou HttpClient?

J'essaie de télécharger le fichier à partir d'une URL et je dois choisir entre WebClient et HttpClient. J'ai référencé this article et plusieurs autres articles sur Internet. Partout, il est suggéré d’utiliser HttpClient en raison de son excellent support async et de ses autres privilèges .Net 4.5. Mais je ne suis toujours pas totalement convaincu et j'ai besoin de plus d'intrants. 

J'utilise le code ci-dessous pour télécharger le fichier à partir d'Internet:

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())
    {
    }
}

De mon point de vue, je ne vois qu'un seul inconvénient à utiliser WebClient, à savoir l'appel non asynchrone bloquant le thread appelant. Mais que se passe-t-il si je ne m'inquiète pas du blocage du thread ou de l'utilisation de client.DownloadFileAsync() pour tirer parti du support async? 

D'autre part, si j'utilise HttpClient, ne puis-je pas charger chaque octet d'un fichier en mémoire et l'écrire ensuite dans un fichier local? Si la taille du fichier est trop importante, la surcharge de mémoire ne sera-t-elle pas coûteuse? Ce qui pourrait être évité si nous utilisons WebClient, car il écrit directement dans un fichier local et ne consomme pas de mémoire système.

Donc, si la performance est ma priorité absolue, quelle approche dois-je utiliser pour le téléchargement? Je voudrais être clarifié si mon hypothèse ci-dessus est fausse, et je suis également ouvert à une approche alternative.

9
Saket

Voici mon approche. 

Si vous appelez un WebApi pour obtenir un fichier, vous pouvez utiliser une requête GET HttpClient GET et renvoyer le flux de fichiers à l'aide du type de retour 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

Voici une façon de l'utiliser pour télécharger une URL et l'enregistrer dans un fichier: (J'utilise Windows 7, donc pas de Windows RT disponible, donc j'utilise aussi 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);
                }
            }
        }
    }
} 

Notez que le code enregistre l’adresse du serveur proxy que j’utilise (au travail) dans un paramètre, et l’utilise si un tel paramètre est spécifié. Sinon, il devrait vous indiquer tout ce que vous devez savoir sur l’utilisation de la version bêta de HttpClient pour télécharger et enregistrer un fichier.

comme suit: this link:

1
Nirzar

Vous pouvez le faire en mode natif avec .Net 4.5+. J'ai essayé de le faire à votre façon et je viens de trouver une méthode dans Intellisense qui semblait avoir du sens.

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

Pour le code appelé de manière répétée, vous ne voulez pas vouloir mettre HttpClient dans un bloc using ( il laissera les ports suspendus ouverts )

Pour télécharger un fichier avec HttpClient, j’ai trouvé cette méthode d’extension qui me semblait une solution efficace et fiable:

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