W3docs

Javascript Fetch : progression du téléchargement

Apprenez à suivre la progression d'un téléchargement avec l'API Fetch en JavaScript grâce aux ReadableStreams et à Content-Length.

L'API Fetch est la méthode moderne pour effectuer des requêtes réseau en JavaScript, mais await fetch(...) se résout dès que les en-têtes de la réponse arrivent — bien avant que le corps ait fini de se télécharger. Pour afficher une barre de progression pour un fichier volumineux, vous devez lire le corps de façon incrémentielle et compter les octets au fur et à mesure de leur arrivée. Ce chapitre explique comment faire exactement cela avec un ReadableStream, pourquoi Content-Length est important, comment construire une interface de progression, et ce que fetch ne peut pas faire (progression de l'envoi).

Pourquoi fetch a besoin d'un flux pour la progression

Lorsque vous écrivez const data = await response.json(), le navigateur met en mémoire tampon l'intégralité du corps en interne et ne le restitue que lorsqu'il est complet — il n'existe aucun hook pour observer les octets en cours de transit. Le corps de la Response est cependant exposé sous forme de ReadableStream via response.body. En lisant vous-même les morceaux de ce flux, vous pouvez mesurer la quantité de données reçues et mettre à jour l'interface à chaque morceau.

La recette est toujours la même :

  1. Obtenir un lecteur : const reader = response.body.getReader().
  2. Boucler sur reader.read(), qui se résout en { done, value }value est un morceau Uint8Array.
  3. Ajouter value.length à un total cumulé et signaler la progression.
  4. Conserver les morceaux, puis les réassembler une fois que done vaut true.

Lire le corps en tant que ReadableStream

Pour connaître le pourcentage, vous avez également besoin de la taille totale. Le serveur devrait envoyer un en-tête de réponse Content-Length ; lisez-le avec response.headers.get('Content-Length'). Il est souvent absent (encodage de transfert fragmenté, compression gzip, ou un serveur qui l'omet simplement), donc l'exemple ci-dessous utilise une taille fournie par l'appelant en cas de secours.

javascript— editable

La fonction fetchWithProgress récupère des données depuis une URL spécifiée et suit la progression du téléchargement. Elle lit le corps de la réponse en morceaux via l'interface ReadableStream et appelle le callback onProgress avec la longueur reçue et la longueur totale du contenu. Les données téléchargées sont ensuite reconstituées à partir des morceaux et renvoyées sous forme de chaîne décodée.

Note

La fonction tente de lire automatiquement l'en-tête Content-Length. Si l'en-tête est absent (fréquent avec les transferts fragmentés ou la compression), elle utilise le paramètre fallbackSize en secours. En production, le développeur ou le serveur doit fournir cette taille de secours. Pour les fichiers volumineux, évitez de mettre tous les morceaux en mémoire tampon ; traitez-les de façon incrémentielle ou utilisez response.blob() pour les réponses non textuelles.

Une fois la boucle terminée, vous disposez d'un array de morceaux Uint8Array. Pour les convertir en données utilisables, copiez-les dans un buffer unique et décodez-les (pour le texte) ou enveloppez-les dans un Blob (pour les fichiers binaires, images, téléchargements). Consultez JavaScript Blob pour travailler avec des données binaires et déclencher des téléchargements de fichiers.

Afficher la progression aux utilisateurs

Une barre de progression n'est qu'un élément dont la largeur suit received / total. Passez un callback updateProgressBar à la place du callback de journalisation :

<body>
  <div id="progress-bar" style="width: 100%; background-color: #e0e0e0;">
    <div id="progress" style="width: 0; height: 20px; background-color: #76c7c0;"></div>
  </div>
  <div id="output"></div>
  <script>
    function updateProgressBar(received, total) {
      const progressElement = document.getElementById('progress');
      const percentage = Math.min(100, (received / total) * 100);
      progressElement.style.width = percentage + '%';
    }
    document.addEventListener('DOMContentLoaded', () => {
      const url = 'https://api.w3docs.com/uploads/media/default/0001/05/dd10c28a7052fb6d2ff13bc403842b797a73ff3b.txt';
      const size = 3_900_000; // fallback size
      // fetchWithProgress is defined in the previous code block
      fetchWithProgress(url, updateProgressBar, size)
      .then(data => {
        document.getElementById('output').textContent = 'File content: ' + data.slice(0, 1000) + '...';
      })
      .catch(err => console.error("Download failed:", err));
    });
  </script>
</body>
Avertissement

Si vous téléchargez un fichier très volumineux, évitez de mettre à jour l'interface trop fréquemment. Par exemple, au lieu de mettre à jour la barre de progression pour chaque morceau, vous pouvez la mettre à jour moins souvent (par exemple, tous les quelques morceaux ou selon un intervalle de temps). Cela permet de maintenir votre interface légère.

Les éléments HTML de l'exemple ci-dessus configurent une barre de progression pour visualiser la progression du téléchargement et une zone de texte préformatée pour afficher le contenu téléchargé. La div progress-bar sert de conteneur, et la div progress représente la progression réelle du téléchargement.

Le code JavaScript met à jour la barre de progression en fonction de la longueur des données reçues et de la longueur totale du contenu. La fonction updateProgressBar calcule le pourcentage de données téléchargées et ajuste la largeur de la barre de progression en conséquence. L'écouteur d'événements déclenche la fonction fetchWithProgress au chargement de la page, en mettant à jour la barre de progression et en affichant le contenu téléchargé dans l'élément output.

Remarque : fetchWithProgress est définie dans l'extrait précédent. Dans un projet réel, assurez-vous qu'elle est dans la portée (par exemple, via des imports de modules, un bundler ou une balise script globale).

Ce que fetch ne peut pas faire : la progression de l'envoi

La technique de flux ne suit que la progression du téléchargement (réponse). À ce jour, il n'existe pas de moyen standard d'observer la progression de l'envoi (corps de la requête) avec fetch — le corps de la requête n'est pas exposé en tant que flux observable dans les navigateurs. Si vous avez besoin d'une barre de progression lors de l'envoi d'un fichier, revenez à XMLHttpRequest, dont l'objet upload déclenche des événements progress :

function uploadWithProgress(url, file, onProgress) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url);

    // upload.onprogress fires repeatedly while the body is sent
    xhr.upload.onprogress = (event) => {
      if (event.lengthComputable) {
        const percent = Math.round((event.loaded / event.total) * 100);
        onProgress(event.loaded, event.total, percent);
      }
    };

    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(new Error('Upload failed'));
    xhr.send(file);
  });
}

event.lengthComputable vous indique si event.total est connu ; vérifiez-le toujours avant de calculer un pourcentage.

Annuler un téléchargement en cours

Un téléchargement long devrait pouvoir être annulé. Passez un AbortSignal à fetch et appelez controller.abort() pour arrêter à la fois la requête et la boucle de flux — voir Fetch : Abort pour le schéma complet.

Conseils professionnels

  • Gérer les données binaires : TextDecoder ne fonctionne que pour le texte. Utilisez response.blob() ou response.arrayBuffer() pour les fichiers binaires.
  • Limiter les mises à jour de progression : Des lectures de morceaux rapides peuvent bloquer le thread de l'interface. Réglez ou anti-rebondissez le callback de progression.
  • Gestion de la mémoire : Évitez de stocker tous les morceaux dans un array pour les fichiers volumineux. Traitez-les de façon incrémentielle.
  • Valeur de secours pour Content-Length : L'en-tête est souvent absent. Fournissez toujours un fallbackSize fiable.
  • Optimiser les retours utilisateur : Fournir un retour en temps réel sur la progression du téléchargement améliore la satisfaction des utilisateurs et peut rendre votre application plus réactive.
  • Exploiter les capacités du navigateur : Différents navigateurs peuvent avoir un support variable pour les fonctionnalités avancées. Testez votre implémentation sur plusieurs navigateurs pour garantir la compatibilité.
  • Simplifier quand c'est possible : Pour les téléchargements où la progression en streaming n'est pas strictement nécessaire, response.arrayBuffer() offre un moyen plus simple de reconstruire les données sans concaténer manuellement les morceaux.

Grâce à ces explications et exemples, vous êtes maintenant équipé pour implémenter l'API Fetch avec le suivi de la progression du téléchargement dans vos projets, offrant aux utilisateurs une expérience fluide et informative.

Conclusion

Maîtriser l'API Fetch implique de comprendre non seulement comment effectuer des requêtes de base, mais aussi comment gérer des scénarios plus avancés comme le suivi de la progression du téléchargement. En utilisant les ReadableStreams, nous pouvons surveiller les téléchargements et fournir des retours, améliorant considérablement l'expérience utilisateur. L'implémentation de ces techniques garantira que vos applications sont robustes, conviviales et capables de gérer efficacement les transferts de données volumineux.

Pratique

Pratique
Quelles sont les étapes clés pour suivre la progression du téléchargement avec l'API Fetch ?
Quelles sont les étapes clés pour suivre la progression du téléchargement avec l'API Fetch ?
Was this page helpful?