Loupe

HttpClient - connaître et afficher l'avancement d'un téléchargement.

Il est courant de devoir télécharger des fichiers et de vouloir afficher l'avancement de ces opérations à l'utilisateur. Dans cet article, nous verrons comment le faire de manière universelle en utilisant HttpClient !

Le package System.Net.HttpClient est universel et fonctionne sous quasiment toutes les plateformes connues. Il expose des méthodes très simples  pour permettre d'utiliser les différents verbes REST. Aussi, c'est un composant très facilement extensible comme déjà montré dans de nombreux articles de ce blog. C'est lui que nous utiliserons dans le reste de cet article.

Téléchargement classique

Pour télécharger notre fichier, on va faire un GET sur l'URL de celui-ci. On prend soin de spécifier que l'on aimerait compresser le contenu (moins de volume = mieux de manière générale) via l'utilisation d'un HttpClientHandler. Le fichier téléchargé est copié directement dans un stream (à initialiser au préalable) en utilisant la méthode asynchrone CopyToAsync de la réponse.

var handler = new HttpClientHandler
    {
        AutomaticDecompression =
            System.Net.DecompressionMethods.Deflate
            | System.Net.DecompressionMethods.GZip
    };
using (var httpClient = new HttpClient(handler, true))
using (var response = await httpClient
        .GetAsync(TargetEpisode.MediaUrl, 
             _cancellationTokenSource.Token
            .ConfigureAwait(false)
{
    response.EnsureSuccessStatusCode();

    await response.Content.CopyToAsync(targetStream)
   .ConfigureAwait(false);
}

Ce fonctionnement est simple mais ne permet pas de savoir où l'on en est pendant le téléchargement...

Téléchargement avec suivi de la progression

Pour faire ce travail, on va devoir effectuer à la main ce que fait la méthode CopyToAsync : copier par bloc le stream correspondant au téléchargement dans le stream cible. On utilise pour cela un tableau de bytes en guise de buffer. La taille de celui-ci est fixée arbitrairement par nos soins à 8192 bytes mais vous pouvez choisir une valeur différente en fonction de la taille des fichiers téléchargés.

Par défaut HttpClient va télécharger intégralement le contenu avant de vous l'exposer sous la forme de l'objet HttpResponse il va donc falloir lui demander de télécharger uniquement les headers de la réponse et de nous laisser traiter nous mêmes le contenu. Pour cela on passe le paramètre "HttpCompletionOption.ResponseHeadersRead" à la méthode GetAsync.

Le reste du traitement correspond à la copie d'un stream vers l'autre au moyen du buffer :

using (var response = await httpClient
      .GetAsync(TargetEpisode.MediaUrl,
            HttpCompletionOption.ResponseHeadersRead,
            _cancellationTokenSource.Token)
    .ConfigureAwait(false))
{
 //réussite du téléchargement ?
 response.EnsureSuccessStatusCode();

// le serveur indique la taille du fichier ?
 long? expectedSize = response.Content.Headers.ContentLength;

 using (Stream contentStream = await response
     .Content.ReadAsStreamAsync().ConfigureAwait(false))
 {
  long totalRead = 0L;
  var buffer = new byte[8192];
  var somethingMoreToRead = true;

  do
  {
  // lecture sur le flux HTTP
  var read = await contentStream
   .ReadAsync(buffer, 0, buffer.Length)
   .ConfigureAwait(false);

  if (read <= 0)
  {
   somethingMoreToRead = false;
  }
  else
  {
     // copie dans le fichier
     await targetStream
        .WriteAsync(buffer, 0, read)
        .ConfigureAwait(false);

      totalRead += read;

    if (expectedSize.HasValue)
    {
      // calcul de la progression
      Progress = (int)(totalRead*100/ (double)expectedSize.Value);
    }
   }
  }
  while (somethingMoreToRead);
 }
}

Pour obtenir la progression on utilise la taille du contenu téléchargé retournée par le serveur sous la forme d'un header pour calculer la valeur d'une propriété Progress qu'il suffira d'utiliser dans votre interface pour afficher l'avancement de vos téléchargements.

 

Happy coding !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus