I am receiving a ReadableStream from a server, returned from my fetch call.
A ReadableStream is returned but I don't know how to trigger a download from this stage. I can't use the url in an href because it requires an Authorization token.
I don't want to install fs on the client so what options do I have?
  try {
    const res = await fetch(url, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/octet-stream'
      }
    });
    const blob = await res.blob();
    const newBlob = new Blob([blob]);
    const newUrl = window.URL.createObjectURL(newBlob);
    const link = document.createElement('a');
    link.href = newUrl;
    link.setAttribute('download', 'filename');
    document.body.appendChild(link);
    link.click();
    link.parentNode.removeChild(link);
    window.URL.revokeObjectURL(newBlob);
  } catch (error) {
    console.log(error);
  }
Update 1
I converted the file to a Blob, then passed it into a newly generated href. Successfully downloaded a file. The end result was the ReadStream contents as a .txt file.
Meaning stuff like this
x:ÚêÒÓ%¶âÜTb∞\܃
To retrieve data from a JavaScript ReadableStream object, we need to call a conversion method to convert the ReadableStream to the actual data we can use. to make a GET request to https://httpbin.org/ip with fetch . fetch returns a promise that resolves to a ReadableStream object which we assigned to response .
The locked read-only property of the ReadableStream interface returns whether or not the readable stream is locked to a reader. A readable stream can have at most one active reader at a time, and is locked to that reader until it is released. A reader might be obtained using ReadableStream.
Readable Stream. A readable stream lets you read data from a source. The source can be anything. It can be a simple file on your file system, a buffer in memory or even another stream. As streams are EventEmitters , they emit several events at various points.
The ReadableStream interface of the Streams API represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. ReadableStream is a transferable object.
I have found 2 solutions, both worked but I was missing a simple addition to make them work.
The native solution is
  try {
    const res = await fetch(url, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`
      }
    });
    const blob = await res.blob();
    const newBlob = new Blob([blob]);
    const blobUrl = window.URL.createObjectURL(newBlob);
    const link = document.createElement('a');
    link.href = blobUrl;
    link.setAttribute('download', `${filename}.${extension}`);
    document.body.appendChild(link);
    link.click();
    link.parentNode.removeChild(link);
    // clean up Url
    window.URL.revokeObjectURL(blobUrl);
This version is using the npm package steamSaver for anyone who would prefer it.
  try {
    const res = await fetch(url, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`
      }
    });
    const fileStream = streamSaver.createWriteStream(`${filename}.${extension}`);
    const writer = fileStream.getWriter();
    const reader = res.body.getReader();
    const pump = () => reader.read()
      .then(({ value, done }) => {
        if (done) writer.close();
        else {
          writer.write(value);
          return writer.ready.then(pump);
        }
      });
    await pump()
      .then(() => console.log('Closed the stream, Done writing'))
      .catch(err => console.log(err));
The key for why it was not working was because I did not include the extension, so it either errored out because of the mimetype was wrong or it opens a .txt file with a string of the body instead of the image.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With