How can I download a file using window.fetch?

JavascriptFetch Api

Javascript Problem Overview


If I want to download a file, what should I do in the then block below?

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(...);
}

Note the codes are in client-side.

Javascript Solutions


Solution 1 - Javascript

EDIT: syg answer is better. Just use downloadjs library.

The answer I provided works well on Chrome, but on Firefox and IE you need some different variant of this code. It's better to use library for that.


I had similar problem (need to pass authorization header to download a file so this solution didn't helped).

But based on this answer you can use createObjectURL to make browser save a file downloaded by Fetch API.

getAuthToken()
    .then(token => {
        fetch("http://example.com/ExportExcel", {
            method: 'GET',
            headers: new Headers({
                "Authorization": "Bearer " + token
            })
        })
        .then(response => response.blob())
        .then(blob => {
            var url = window.URL.createObjectURL(blob);
            var a = document.createElement('a');
            a.href = url;
            a.download = "filename.xlsx";
            document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
            a.click();    
            a.remove();  //afterwards we remove the element again         
        });
    });

Solution 2 - Javascript

This is more shorter and efficient, no libraries only fetch API

const url ='http://sample.example.file.doc'
const authHeader ="Bearer 6Q************" 

const options = {
  headers: {
    Authorization: authHeader
  }
};
 fetch(url, options)
  .then( res => res.blob() )
  .then( blob => {
    var file = window.URL.createObjectURL(blob);
    window.location.assign(file);
  });

This solution does not allow you to change filename for the downloaded file. The filename will be a random uuid.

Solution 3 - Javascript

I temporarily solve this problem by using download.js and blob.

let download = require('./download.min');

...

function downloadFile(token, fileId) {
  let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
  return fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': token
    }
  }).then(function(resp) {
    return resp.blob();
  }).then(function(blob) {
    download(blob);
  });
}

It's working for small files, but maybe not working for large files. I think I should dig Stream more.

Solution 4 - Javascript

function download(dataurl, filename) {
  var a = document.createElement("a");
  a.href = dataurl;
  a.setAttribute("download", filename);
  a.click();
  return false;
}

download("data:text/html,HelloWorld!", "helloWorld.txt");

or:

function download(url, filename) { fetch(url).then(function(t) { return t.blob().then((b)=>{ var a = document.createElement("a"); a.href = URL.createObjectURL(b); a.setAttribute("download", filename); a.click(); } ); }); }

download("https://get.geojs.io/v1/ip/geo.json","geoip.json";) download("data:text/html,HelloWorld!", "helloWorld.txt");

Solution 5 - Javascript

Using dowloadjs. This will parse the filename from the header.

fetch("yourURL", {
    method: "POST",
    body: JSON.stringify(search),
    headers: {
        "Content-Type": "application/json; charset=utf-8"
    }
    })
    .then(response => {
        if (response.status === 200) {
            filename = response.headers.get("content-disposition");
            filename = filename.match(/(?<=")(?:\\.|[^"\\])*(?=")/)[0];
            return response.blob();
        } else {
        return;
        }
    })
    .then(body => {
        download(body, filename, "application/octet-stream");
    });
};

Solution 6 - Javascript

Here is an example using node-fetch for anyone that finds this.

reportRunner({url, params = {}}) {
    let urlWithParams = `${url}?`
    Object.keys(params).forEach((key) => urlWithParams += `&${key}=${params[key]}`)
    return fetch(urlWithParams)
        .then(async res => ({
            filename: res.headers.get('content-disposition').split('filename=')[1],
            blob: await res.blob()
        }))
        .catch(this.handleError)
}

Solution 7 - Javascript

As per some of the other answers, you can definitely use window.fetch and download.js to download a file. However, using window.fetch with blob has the restriction on memory imposed by the browser, and the download.js also has its compatibility restrictions.

If you need to download a big-sized file, you don't want to put it in the memory of the client side to stress the browser, right? Instead, you probably prefer to download it via a stream. In such a case, using an HTML link to download a file is one of the best/simplest ways, especially for downloading big-sized files via a stream.

Step One: create and style a link element

You can make the link invisible but still actionable.

HTML:

<a href="#" class="download-link" download>Download</a>

CSS:

.download-link {
  position: absolute;
  top: -9999px;
  left: -9999px;
  opacity: 0;
}

Step Two: Set the href of the link, and trigger the click event

JavaScript

let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;

const downloadLink = document.querySelector('.download-link')
downloadLink.href = url + '&ts=' + new Date().getTime() // Prevent cache
downloadLink.click()

Notes:

  • You can dynamically generate the link element if necessary.

  • This approach is especially useful for downloading, via a stream, big-sized files that are dynamically generated on the server side

Solution 8 - Javascript

I tried window.fetch but that ended up being complicated with my REACT app

now i just change window.location.href and add query params like the jsonwebtoken and other stuff.


///==== client side code =====
var url = new URL(`http://${process.env.REACT_APP_URL}/api/mix-sheets/list`);
url.searchParams.append("interval",data.interval);
url.searchParams.append("jwt",token)

window.location.href=url;

// ===== server side code =====

// on the server i set the content disposition to a file
var list = encodeToCsv(dataToEncode);
res.set({"Content-Disposition":`attachment; filename=\"FileName.csv\"`});
res.status(200).send(list)

the end results actually end up being pretty nice, the window makes request and downloads the file and doesn't event switch move the page away, its as if the window.location.href call was like a lowkey fetch() call.

Solution 9 - Javascript

A similar but cleaner and more reliable solution IMO.

On your fetch function...

fetch(...)    
.then(res => 
    {
        //you may want to add some validation here
        downloadFile(res);
    }
)

and the downloadFile function is...

async function downloadFile(fetchResult) {        
    var filename = fetchResult.headers.get('content-disposition').split('filename=')[1];
    var data = await fetchResult.blob();
    // It is necessary to create a new blob object with mime-type explicitly set
    // otherwise only Chrome works like it should
    const blob = new Blob([data], { type: data.type || 'application/octet-stream' });
    if (typeof window.navigator.msSaveBlob !== 'undefined') {
        // IE doesn't allow using a blob object directly as link href.
        // Workaround for "HTML7007: One or more blob URLs were
        // revoked by closing the blob for which they were created.
        // These URLs will no longer resolve as the data backing
        // the URL has been freed."
        window.navigator.msSaveBlob(blob, filename);
        return;
    }
    // Other browsers
    // Create a link pointing to the ObjectURL containing the blob
    const blobURL = window.URL.createObjectURL(blob);
    const tempLink = document.createElement('a');
    tempLink.style.display = 'none';
    tempLink.href = blobURL;
    tempLink.setAttribute('download', filename);
    // Safari thinks _blank anchor are pop ups. We only want to set _blank
    // target if the browser does not support the HTML5 download attribute.
    // This allows you to download files in desktop safari if pop up blocking
    // is enabled.
    if (typeof tempLink.download === 'undefined') {
        tempLink.setAttribute('target', '_blank');
    }
    document.body.appendChild(tempLink);
    tempLink.click();
    document.body.removeChild(tempLink);
    setTimeout(() => {
        // For Firefox it is necessary to delay revoking the ObjectURL
        window.URL.revokeObjectURL(blobURL);
    }, 100);
}

(downloadFile function source: https://gist.github.com/davalapar/d0a5ba7cce4bc599f54800da22926da2)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzachguoView Question on Stackoverflow
Solution 1 - JavascriptMariusz PawelskiView Answer on Stackoverflow
Solution 2 - JavascriptLucas MatosView Answer on Stackoverflow
Solution 3 - JavascriptzachguoView Answer on Stackoverflow
Solution 4 - JavascriptZibriView Answer on Stackoverflow
Solution 5 - JavascriptDanielView Answer on Stackoverflow
Solution 6 - JavascriptMichael HobbsView Answer on Stackoverflow
Solution 7 - JavascriptYuciView Answer on Stackoverflow
Solution 8 - JavascriptMaddocksView Answer on Stackoverflow
Solution 9 - JavascriptlemonskunnkView Answer on Stackoverflow