Fetch API to force download file
Asked Answered
W

5

15

I'm calling an API to download excel file from the server using the fetch API but it didn't force the browser to download, below is my header response:

HTTP/1.1 200 OK Content-Length: 168667 
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 
Server: Microsoft-IIS/8.5 
Content-Disposition: attachment; filename=test.xlsx 
Access-Control-Allow-Origin: http://localhost:9000 
Access-Control-Allow-Credentials: true 
Access-Control-Request-Method: POST,GET,PUT,DELETE,OPTIONS 
Access-Control-Allow-Headers: X-Requested-With,Accept,Content-Type,Origin 
Persistent-Auth: true 
X-Powered-By: ASP.NET 
Date: Wed, 24 May 2017 20:18:04 GMT

Below my code that I'm using to call the API :

this.httpClient.fetch(url, {
    method: 'POST',
    body: JSON.stringify(object),
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }
})
Wakerobin answered 24/5, 2017 at 20:40 Comment(6)
You probably want to use stackoverflow.com/posts/44168090/edit to update/edit your question to add a snippet of the code you are using to try to download the fileFlorinda
How are you "calling an API"?Indictable
this.httpClient.fetch(url, { method: 'POST', body: JSON.stringify(object), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } })Wakerobin
I found away to download using the blob method but if there is another way to force download ?Wakerobin
As a security feature - it's not possible for JavaScript to start a download to your machine using AJAX - as it doesn't have access to write files to your computer.Indictable
related: https://mcmap.net/q/660192/-how-can-i-download-and-save-a-file-using-the-fetch-api-node-jsLycopodium
L
26

The browser won't show the usual interaction for the download (display Save As... dialog, etc.), only if you navigate to that resource. It is easier to show the difference with an example:

  1. window.location='http://mycompany.com/'
  2. Load http://mycompany.com/ via XHR/Fetch in the background.

In 1., the browser will load the page and display its content. In 2., the browser will load the raw data and return it to you, but you have to display it yourself.

You have to do something similar with files. You have the raw data, but you have to "display" it yourself. To do this, you need to create an object-URL for your downloaded file and navigate to it:

this.httpClient
    .fetch(url, {method, body, headers})
    .then(response => response.blob())
    .then(blob => URL.createObjectURL(blob))
    .then(url => {
        window.open(url, '_blank');
        URL.revokeObjectURL(url);
    });

This fetches the response, reads it as a blob, creates an objectURL, opens it (in a new tab), then revokes the URL.

More about object-URLs: https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL

Lunulate answered 7/6, 2017 at 21:16 Comment(1)
this answer will open a new tab to preview the image. But because it's using URL.revokeObjectURL(url); the image can't be downloadedRexanna
A
8

My solution is based on @VINEE and @balazska solution. I wanted to avoid manipulating document.body or opening a new tab via window.open(url, '_blank');

return fetch(urlEndpoint, options)
  .then((res) => res.blob())
  .then((blob) => URL.createObjectURL(blob))
  .then((href) => {
    Object.assign(document.createElement('a'), {
      href,
      download: 'filename.csv',
    }).click();
  });
Admissive answered 25/3, 2021 at 21:17 Comment(0)
U
5

There are some handy libraries and to solve an issue that I had with CSV/text download I used FileSaver.

Example:

var saveAs = require('file-saver');

fetch('/download/urf/file', {
  headers: {
    'Content-Type': 'text/csv'
  },
  responseType: 'blob'
}).then(response => response.blob())
  .then(blob => saveAs(blob, 'test.csv'));

There is also download.js lib as explained here in this question.

Urbanus answered 5/2, 2018 at 11:29 Comment(3)
Why would you set the request header "Content-Type: application/pdf" if you're making a GET request?Hypercorrect
@Hypercorrect Request headers objects have a guard property. This is not exposed to the Web, but it affects which mutation operations are allowed on the headers object. Found at developer.mozilla.org/en-US/docs/Web/API/Fetch_API/…Urbanus
I still don't think that explains why you would set a Content-Type on a GET request which has no body. What is the meaning/purpose of the Content-Type in this case?Hypercorrect
N
5

You can do it like this using the below function

download(filename) {
 fetch(url , { headers })
 .then(response => response.blob())
 .then(blob => URL.createObjectURL(blob))
 .then(uril => {
 var link = document.createElement("a");
 link.href = uril;
 link.download = filename + ".csv";
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);
 });
}

here I want to download a CSV file, So I add .csv to the filename.

Nicolas answered 6/2, 2020 at 21:20 Comment(0)
W
0

I found another way to download and it will work on IE by using

https://www.npmjs.com/package/downloadjs

Wakerobin answered 8/8, 2017 at 13:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.