Show a Progress Indicator for a Fetch Request with the Streams API

AnthumChris collected some JavaScript scripts to track the progress of fetch requests that download files/data. It works by leveraging the ReadableStream interface from the Streams API.

A “simple” example is this:

fetch('https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg')
.then(response => {
  if (!response.ok) {
    throw Error(response.status+' '+response.statusText)
  }

  if (!response.body) {
    throw Error('ReadableStream not yet supported in this browser.')
  }

  // to access headers, server must send CORS header "Access-Control-Expose-Headers: content-encoding, content-length x-file-size"
  // server must send custom x-file-size header if gzip or other content-encoding is used
  const contentEncoding = response.headers.get('content-encoding');
  const contentLength = response.headers.get(contentEncoding ? 'x-file-size' : 'content-length');
  if (contentLength === null) {
    throw Error('Response size header unavailable');
  }

  const total = parseInt(contentLength, 10);
  let loaded = 0;

  return new Response(
    new ReadableStream({
      start(controller) {
        const reader = response.body.getReader();

        read();
        function read() {
          reader.read().then(({done, value}) => {
            if (done) {
              controller.close();
              return; 
            }
            loaded += value.byteLength;
            console.log(Math.round(loaded/total*100)+'%');
            controller.enqueue(value);
            read();
          }).catch(error => {
            console.error(error);
            controller.error(error)                  
          })
        }
      }
    })
  );
})
.then(response => response.blob())
.then(data => {
  console.log('download completed');
  // document.getElementById('img').src = URL.createObjectURL(data);
})
.catch(error => {
  console.error(error);
});

If that code confuses you, it’s mostly the Fetch + ReadableStream example code from MDN. The key additions are:

  • Extract the length from the content-length header. If some encoding is used, the server must send the x-file-size header instead
  • Store total bytes and loaded
  • Increment loaded and calculate the progress (here: logged to the console)
  • Do something with the data (here: commented out)

Can also be used from within a ServiceWorker.

Fetch & Streams API Progress Indicator Source Code (GitHub) →
Fetch & Streams API Progress Indicator Demos →

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.