score:19

Accepted answer

When you use the fetch API, your response object has a bunch of methods to handle the data that you get. The most used is json(). If you need to download a file coming from the server what you need is blob(), which works the same way as json().

response.blob().then(blob => download(blob))

There are a lot of npm packages to download files. file-saver is one of them. One way that works without dependencies though is by using the a tag. Something like that:

function download(blob, filename) {
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  // the filename you want
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  window.URL.revokeObjectURL(url);
}

Anyway, using a dependency would cover more edge cases, and it's usually more compatible. I hope that can be useful

If you want to show the pdf in another tab instead of downloading it, you would use window.open and pass the URL generated by window.URL.createObjectURL.

function showInOtherTab(blob) {
  const url = window.URL.createObjectURL(blob);
  window.open(url);
}

score:4

If the end point a stream, one solution is retrieve the whole file then to save it.

Disclaimer: the solution I offer below involve loading a whole pdf file in memory and avoid opening a new window tab to download the file (using downloadjs: very useful to rename and save the downloaded data). If anyone get a solution to avoid loading the whole file in memory before saving it, fell free to share :) :).

Here is the code:

import download from 'downloadjs'

fetch(...)
  .then(response => {
    //buffer to fill with all data from server
    let pdfContentBuffer = new Int8Array();

    // response.body is a readableStream 
    const reader = response.body.getReader();

    //function to retreive the next chunk from the stream
    function handleChunk({ done, chunk })  {
      if (done) {
        //everything has been loaded, call `download()` to save gthe file as pdf and name it "my-file.pdf"
        download(pdfContentBuffer, `my-file.pdf`, 'application/pdf')
        return;
      }

      // concat already loaded data with the loaded chunk
      pdfContentBuffer = Int8Array.from([...pdfContentBuffer, ...chunk]);

      // retreive next chunk
      reader.read().then(handleChunk);
    }

    //retreive first chunk
    reader.read().then(handleChunk)
  })
  .catch(err => console.error(err))

I share it hoping this will help others.


Related Query

More Query from same tag