Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
553 views
in Technique[技术] by (71.8m points)

javascript - How to download a ReadableStream on the browser that has been returned from fetch

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:úêòó%?aüTb∞ü?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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);

    window.URL.revokeObjectURL(blob);

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...