How to download files using axios
Asked Answered
M

20

283

I am using axios for basic http requests like GET and POST, and it works well. Now I need to be able to download Excel files too. Is this possible with axios? If so does anyone have some sample code? If not, what else can I use in a React application to do the same?

Mesotron answered 30/1, 2017 at 14:47 Comment(2)
We can use this solution to download the excel file. #57127861Prosecutor
Try to check this link it might help you to solve your problem) https://mcmap.net/q/110051/-how-to-download-file-in-react-jsSteep
D
182

When response comes with a downloadable file, response headers will be something like

Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"

What you can do is create a separate component, which will contain a hidden iframe.

  import * as React from 'react';

  var MyIframe = React.createClass({

     render: function() {
         return (
           <div style={{display: 'none'}}>
               <iframe src={this.props.iframeSrc} />
           </div>
         );
     }
  });

Now, you can pass the url of the downloadable file as prop to this component, So when this component will receive prop, it will re-render and file will be downloaded.

Edit: You can also use js-file-download module. Link to Github repo

const FileDownload = require('js-file-download');

Axios({
  url: 'http://localhost/downloadFile',
  method: 'GET',
  responseType: 'blob', // Important
}).then((response) => {
    FileDownload(response.data, 'report.csv');
});
Disinfection answered 30/1, 2017 at 16:2 Comment(20)
Thank you. Can you tell me if this is in ajax style. It would be good not to block the page.Mesotron
Yes, Page will not be blocked. When you pass the url as prop to that component, file will be downloaded automatically. You won't need to do anything.Disinfection
One more question. In my case the file being downloaded is dynamically created with some parameters passed. So it does not really have a consistent location. What is the url I send for this type of scenario? For example if I called axios.post('api/getmyexcelfile', params);Mesotron
As mentioned in this answer. In axios response object, inside request there is a field named As responseURL, maybe this is the URL that you want.Disinfection
No, I dont see a responseURL. I do see a response and responseText, but both of them have "PK\u0003\u0004\u0014" as the value.Mesotron
Let us continue this discussion in chat.Mesotron
@DavidChoi Hi..I've come across this npm module to download file. npmjs.com/package/react-file-download, you can try it.Disinfection
react-file-download does not support Safari <10.1 as it uses the anchor tag's download property introduced in HTML5. Older versions of Safari don't support it.Nide
Yeah...It's an alternative when the first approach does not work. If you know any workaround for safari <10.1 then you can edit my answer. I'll be more than happy. :)Disinfection
i am using 'react-file-download' but the downloaded file is empty in my case, number of pages is correct though, any idea? axios.post('/url', {id: 1}) .then(response => { FileDownload(response.data, 'test.pdf'); }) ; if I use normal form submit to download then it works fine, so np in back-endKattiekatuscha
Got it working with axios, getting past authentication. Here's the gist: gist.github.com/dreamyguy/6b4ab77d2f118adb8a63c4a03fba349dScrapple
js-download github page is broken.Navigation
Seems like an issue from npm side. The github repo is at github.com/kennethjiang/js-file-downloadDisinfection
@Navigation I have updated an answer with a link to Github repo.Disinfection
I followed this and was able to download the file. But the file is broken (doesn't work). However, if I use redirection ( window.location.href ) the file downloads and works perfectly. Can someone help me with this, please? (#56306508)Upcast
js-file-download works for me (tested with chrome on ubuntu) but it's necessary to specify responseType: 'blob' in the axios get request (thanks @Viney for the "// important" comment in your response!Alfonsoalfonzo
don't forget responseType: blob axios .get(url, { responseType: 'blob' }) .then((response) => { fileDownload(response.data, 'filename.xlsx') })Guessrope
Thank you so much for this line: responseType: 'blob'. It was the missing piece that I neededSchedule
Im downloading the excel file like this. The file gets downloaded but when I open it says file is corrupt. any idea why?Inkstand
worked for me (js-file-download), thanksRetool
A
390
  1. Download the file with Axios as a responseType: 'blob'
  2. Create a file link using the blob in the response from Axios/Server
  3. Create <a> HTML element with a the href linked to the file link created in step 2 & click the link
  4. Clean up the dynamically created file link and HTML element
axios({
    url: 'http://api.dev/file-download', //your url
    method: 'GET',
    responseType: 'blob', // important
}).then((response) => {
    // create file link in browser's memory
    const href = URL.createObjectURL(response.data);

    // create "a" HTML element with href to file & click
    const link = document.createElement('a');
    link.href = href;
    link.setAttribute('download', 'file.pdf'); //or any other extension
    document.body.appendChild(link);
    link.click();

    // clean up "a" element & remove ObjectURL
    document.body.removeChild(link);
    URL.revokeObjectURL(href);
});

Check out the quirks at https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

Full credits to: https://gist.github.com/javilobo8

More documentation for URL.createObjectURL is available on MDN. It's critical to release the object with URL.revokeObjectURL to prevent a memory leak. In the function above, since we've already downloaded the file, we can immediately revoke the object.

Each time you call createObjectURL(), a new object URL is created, even if you've already created one for the same object. Each of these must be released by calling URL.revokeObjectURL() when you no longer need them.

Browsers will release object URLs automatically when the document is unloaded; however, for optimal performance and memory usage, if there are safe times when you can explicitly unload them, you should do so.

Arturoartus answered 30/1, 2017 at 14:47 Comment(16)
Thank you for the solution. Just a few notes for others: While this might work for a lot of use cases, but for large file sizes you will not be able to see the download progress. And it will take extra memory in the browser. As alluded to in other solutions, but not spelled out, general approach is to use header 'Content-Disposition: attachment;' so the browser will treat it as a native download (aforementioned download progress + direct download to disk).Naturism
On the server side even when I set the Content-Desposition header, it doesn't seem allow download progress.Lions
Thanks for this. Was wondering why the file content wasn't appearing correctly. Turns out I was missing responseType: 'blob'Cardinalate
doesn't this download the file as a response first to memory (without the browser actually showing download progress) only when the file is downloaded as a blob in memory then only the browser will attempt to save it to the download file..Chios
@Chios Yes you're right the xhr request will be sent and when the response arrives it will be buffered in memory and later, stored in variable response upon completion. Then createObjectURL creates a local url to this data that an <a> can navigate to.Arturoartus
@Viney hence if it is buffered to memory, downloading a 20gb wont show any progress and will probably exhaust ram (excluding swap) instead of buffering, why not stream the download this way you can also show the download progress which is native?Chios
@Chios As per this streaming to file is currently not supported.Arturoartus
Do we need the line document.body.appendChild(link); ? The solution works for me without that (Chrome)Porterporterage
@JohnLee I get your point but that will not work since it's axios here that is making a xhr request. Only after the entire chunked response is read, are we creating a <a> link. What you are saying (asking server to send the right headers) will work if you provide the download url directly to the href attribute, but those are only for cases where your download file is public.Jiles
@Arturoartus This solution has memory overhead. There is no need to document.body.appendChild(link). Also use window.URL.revokeObjectURL(url) after link.click()Jiles
@Arturoartus you sir are a saint. Such a small, easy to miss thing. responseType: 'blob' does the trickSelfdefense
This idea is also good for videos?Berserk
@TimeBuy Yes it should work for any type of fileArturoartus
URL.revokeObjectURL(url); should be URL.revokeObjectURL(href); as it requires the parameter to be the result of the createObjectURL function.Nitramine
I got this solutions and it is perfect.what confuse me is, how to download a excel without the responseType set as blob or arraybuffer.Logy
I got this solutions and it is perfect.what confuse me is, how to download a excel without the responseType set as blob or arraybuffer. The project is old and i want change the request util from ‘fetch’ to‘Axios’.then the excel can be download by the ‘new bolb and createObj url ’,but the excel can not opened correctlyLogy
D
182

When response comes with a downloadable file, response headers will be something like

Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"

What you can do is create a separate component, which will contain a hidden iframe.

  import * as React from 'react';

  var MyIframe = React.createClass({

     render: function() {
         return (
           <div style={{display: 'none'}}>
               <iframe src={this.props.iframeSrc} />
           </div>
         );
     }
  });

Now, you can pass the url of the downloadable file as prop to this component, So when this component will receive prop, it will re-render and file will be downloaded.

Edit: You can also use js-file-download module. Link to Github repo

const FileDownload = require('js-file-download');

Axios({
  url: 'http://localhost/downloadFile',
  method: 'GET',
  responseType: 'blob', // Important
}).then((response) => {
    FileDownload(response.data, 'report.csv');
});
Disinfection answered 30/1, 2017 at 16:2 Comment(20)
Thank you. Can you tell me if this is in ajax style. It would be good not to block the page.Mesotron
Yes, Page will not be blocked. When you pass the url as prop to that component, file will be downloaded automatically. You won't need to do anything.Disinfection
One more question. In my case the file being downloaded is dynamically created with some parameters passed. So it does not really have a consistent location. What is the url I send for this type of scenario? For example if I called axios.post('api/getmyexcelfile', params);Mesotron
As mentioned in this answer. In axios response object, inside request there is a field named As responseURL, maybe this is the URL that you want.Disinfection
No, I dont see a responseURL. I do see a response and responseText, but both of them have "PK\u0003\u0004\u0014" as the value.Mesotron
Let us continue this discussion in chat.Mesotron
@DavidChoi Hi..I've come across this npm module to download file. npmjs.com/package/react-file-download, you can try it.Disinfection
react-file-download does not support Safari <10.1 as it uses the anchor tag's download property introduced in HTML5. Older versions of Safari don't support it.Nide
Yeah...It's an alternative when the first approach does not work. If you know any workaround for safari <10.1 then you can edit my answer. I'll be more than happy. :)Disinfection
i am using 'react-file-download' but the downloaded file is empty in my case, number of pages is correct though, any idea? axios.post('/url', {id: 1}) .then(response => { FileDownload(response.data, 'test.pdf'); }) ; if I use normal form submit to download then it works fine, so np in back-endKattiekatuscha
Got it working with axios, getting past authentication. Here's the gist: gist.github.com/dreamyguy/6b4ab77d2f118adb8a63c4a03fba349dScrapple
js-download github page is broken.Navigation
Seems like an issue from npm side. The github repo is at github.com/kennethjiang/js-file-downloadDisinfection
@Navigation I have updated an answer with a link to Github repo.Disinfection
I followed this and was able to download the file. But the file is broken (doesn't work). However, if I use redirection ( window.location.href ) the file downloads and works perfectly. Can someone help me with this, please? (#56306508)Upcast
js-file-download works for me (tested with chrome on ubuntu) but it's necessary to specify responseType: 'blob' in the axios get request (thanks @Viney for the "// important" comment in your response!Alfonsoalfonzo
don't forget responseType: blob axios .get(url, { responseType: 'blob' }) .then((response) => { fileDownload(response.data, 'filename.xlsx') })Guessrope
Thank you so much for this line: responseType: 'blob'. It was the missing piece that I neededSchedule
Im downloading the excel file like this. The file gets downloaded but when I open it says file is corrupt. any idea why?Inkstand
worked for me (js-file-download), thanksRetool
P
74

Downloading Files (using Axios and Security)

This is actually even more complex when you want to download files using Axios and some means of security. To prevent anyone else from spending too much time in figuring this out, let me walk you through this.

You need to do 3 things:

  1. Configure your server to permit the browser to see required HTTP headers
  2. Implement the server-side service, and making it advertise the correct file type for the downloaded file.
  3. Implementing an Axios handler to trigger a FileDownload dialog within the browser

These steps are mostly doable - but are complicated considerably by the browser's relation to CORS. One step at a time:

1. Configure your (HTTP) server

When employing transport security, JavaScript executing within a browser can [by design] access only 6 of the HTTP headers actually sent by the HTTP server. If we would like the server to suggest a filename for the download, we must inform the browser that it is "OK" for JavaScript to be granted access to other headers where the suggested filename would be transported.

Let us assume - for the sake of discussion - that we want the server to transmit the suggested filename within an HTTP header called X-Suggested-Filename. The HTTP server tells the browser that it is OK to expose this received custom header to the JavaScript/Axios with the following header:

Access-Control-Expose-Headers: X-Suggested-Filename

The exact way to configure your HTTP server to set this header varies from product to product.

See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers for a full explanation and detailed description of these standard headers.

2. Implement the server-side service

Your server-side service implementation must now perform 2 things:

  1. Create the (binary) document and assign the correct ContentType to the response
  2. Assign the custom header (X-Suggested-Filename) containing the suggested file name for the client

This is done in different ways depending on your chosen technology stack. I will sketch an example using the JavaEE 7 standard which should emit an Excel report:

    @GET
    @Path("/report/excel")
    @Produces("application/vnd.ms-excel")
    public Response getAllergyAndPreferencesReport() {

        // Create the document which should be downloaded
        final byte[] theDocumentData = .... 

        // Define a suggested filename
        final String filename = ... 
     
        // Create the JAXRS response
        // Don't forget to include the filename in 2 HTTP headers: 
        //
        // a) The standard 'Content-Disposition' one, and
        // b) The custom 'X-Suggested-Filename'  
        //
        final Response.ResponseBuilder builder = Response.ok(
                theDocumentData, "application/vnd.ms-excel")
                .header("X-Suggested-Filename", fileName);
        builder.header("Content-Disposition", "attachment; filename=" + fileName);

        // All Done.
        return builder.build();
    }

The service now emits the binary document (an Excel report, in this case), sets the correct content type - and also sends a custom HTTP header containing the suggested filename to use when saving the document.

3. Implement an Axios handler for the Received document

There are a few pitfalls here, so let's ensure all details are correctly configured:

  1. The service responds to @GET (i.e. HTTP GET), so the Axios call must be 'axios.get(...)'.
  2. The document is transmitted as a stream of bytes, so you must tell Axios to treat the response as an HTML5 Blob. (I.e. responseType: 'blob').
  3. In this case, the file-saver JavaScript library is used to pop the browser dialog open. However, you could choose another.

The skeleton Axios implementation would then be something along the lines of:

     // Fetch the dynamically generated excel document from the server.
     axios.get(resource, {responseType: 'blob'}).then((response) => {

        // Log somewhat to show that the browser actually exposes the custom HTTP header
        const fileNameHeader = "x-suggested-filename";
        const suggestedFileName = response.headers[fileNameHeader];
        const effectiveFileName = (suggestedFileName === undefined
                    ? "allergierOchPreferenser.xls"
                    : suggestedFileName);
        console.log(`Received header [${fileNameHeader}]: ${suggestedFileName}, effective fileName: ${effectiveFileName}`);

        // Let the user save the file.
        FileSaver.saveAs(response.data, effectiveFileName);

        }).catch((response) => {
            console.error("Could not Download the Excel report from the backend.", response);
        });
Prescind answered 26/8, 2017 at 13:21 Comment(7)
What's "FileSaver"?Prothonotary
It's a library to handle download files, github.com/eligrey/FileSaver.js/#filesaverjsQuartermaster
This works, but it is recommended to use content-disposition header instead of x-suggested-filename.Galimatias
This is good, but it misses a core point that while performing axios.get(resource, {responseType: 'blob'}).then((response) => () => {}) using blob as responseType, the entire file is loaded to memory and then processed, even if the server is transferring the data as chunks. This is critical because you will have to end up implementing your own progress bar, since you cannot make use of the native one provided by browsers. This is a shortcoming of xhr itself.Jiles
@KJSudarshan do you know what is the solution for large files? i'm currently encountering this problem :(Loupgarou
@KJSudarshan I'm facing the same problem - the file is loaded to memory & then processed. How can we change this to use native browser support?Lagos
I set a file name and it opens the save dialog with _ before and after the file name I definedCameo
M
38

Axios.post solution with IE and other browsers

I've found some incredible solutions here. But they frequently don't take into account problems with IE browser. Maybe it will save some time to somebody else.

axios.post("/yourUrl",
    data,
    { responseType: 'blob' }
).then(function (response) {
    let fileName = response.headers["content-disposition"].split("filename=")[1];
    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
        window.navigator.msSaveOrOpenBlob(new Blob([response.data],
                { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
            ),
            fileName
        );
    } else {
        const url = window.URL.createObjectURL(new Blob([response.data],
            { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download',
            response.headers["content-disposition"].split("filename=")[1]);
        document.body.appendChild(link);
        link.click();
    }
    }
);

example above is for excel files, but with little changes can be applied to any format.

And on server I've done this to send an excel file.

response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
Melodramatic answered 24/5, 2019 at 11:51 Comment(0)
S
26

The function to make the API call with axios:

function getFileToDownload (apiUrl) {
   return axios.get(apiUrl, {
     responseType: 'arraybuffer',
     headers: {
       'Content-Type': 'application/json'
     }
   })
}

Call the function and then download the excel file you get:

getFileToDownload('putApiUrlHere')
  .then (response => {
      const type = response.headers['content-type']
      const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' })
      const link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'file.xlsx'
      link.click()
  })
Strongarm answered 23/7, 2019 at 12:23 Comment(5)
hey Im doing this but my excel file is corrupted, any idea why?Inkstand
@habiba maybe the response file from the backend is wrongStrongarm
Im using drf for the backend and when I save the response using postman. It saves it to an excel file and it opens perfect with all the data @roli roliInkstand
@habiba It works perfectly to me. Maybe you have to change the encoding to whatever the encoding of the file is.Connacht
"Object literal may only specify known properties, but 'encoding' does not exist in type 'BlobPropertyBag'. Did you mean to write 'endings'?"Plasm
E
15
        axios.get(
            '/app/export'
        ).then(response => {    
            const url = window.URL.createObjectURL(new Blob([response]));
            const link = document.createElement('a');
            link.href = url;
            const fileName = `${+ new Date()}.csv`// whatever your file name .
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            link.click();
            link.remove();// you need to remove that elelment which is created before.
})
Easygoing answered 31/1, 2019 at 8:50 Comment(0)
I
15

It's very simple javascript code to trigger a download for the user:

window.open("<insert URL here>")

You don't want/need axios for this operation; it should be standard to just let the browser do it's thing.

Note: If you need authorisation for the download then this might not work. I'm pretty sure you can use cookies to authorise a request like this, provided it's within the same domain, but regardless, this might not work immediately in such a case.


As for whether it's possible... not with the in-built file downloading mechanism, no.

Intensify answered 24/6, 2019 at 8:56 Comment(5)
Authorization headers?Gomorrah
What if you need to send a token?Cockswain
If you control the server, then you can simply store the access token as a cookie, and the browser will add it to any request to your server. medium.com/@ryanchenkie_40935/…Intensify
this gives the best user experience IMO. It's a hassle to set up authorization in cookies but worth it. Client-side you'll need axios.defaults.withCredentials = true. Server-side is where the heavy-lifting is done.Wagonlit
This is the best and simplest solution for pre-signed URL. Thanks!Halsey
J
12

There are a couple of critical points most of the answers are missing.

I will try to explain in much depth here.

TLDR;

If you are creating an a tag link and initiating a download through broswer request, then

  1. Always call window.URL.revokeObjectURL(url);. Else there can be unnecessary memory spikes.

  2. There is NO need to append the created link to the document body using document.body.appendChild(link);, preventing the unnecessary need to remove the child later.


For Component code and a deeper analysis, read further

First is to figure out if the API endpoint from which you are trying to download the data is public or private. Do you have control over the server or not?


If the server responds with

Content-Disposition: attachment; filename=dummy.pdf
Content-Type: application/pdf

Browser will always try to download the file with the name 'dummy.pdf'


If the server responds with

Content-Disposition: inline; filename=dummy.pdf
Content-Type: application/pdf

Browser will first try to open a native file reader if available with the name 'dummy.pdf', else it will start file download.


If the server responds with neither of the above 2 headers

Browser (atleast chrome) will try to open the file if the download attribute is not set. If set, it will download the file. The name of the file will be the value of the last path param in cases where the url is not a blob.


Apart from that keep in mind to use Transfer-Encoding: chunked from server to transfer large volumes of data from the server. This will ensure the client knows when to stop reading from the current request in the absence of Content-Length header

For Private Files

import { useState, useEffect } from "react";
import axios from "axios";

export default function DownloadPrivateFile(props) {
  const [download, setDownload] = useState(false);

  useEffect(() => {
    async function downloadApi() {
      try {
        // It doesn't matter whether this api responds with the Content-Disposition header or not
        const response = await axios.get(
          "http://localhost:9000/api/v1/service/email/attachment/1mbdoc.docx",
          {
            responseType: "blob", // this is important!
            headers: { Authorization: "sometoken" },
          }
        );
        const url = window.URL.createObjectURL(new Blob([response.data])); // you can mention a type if you wish
        const link = document.createElement("a");
        link.href = url;
        link.setAttribute("download", "dummy.docx"); //this is the name with which the file will be downloaded
        link.click();
        // no need to append link as child to body.
        setTimeout(() => window.URL.revokeObjectURL(url), 0); // this is important too, otherwise we will be unnecessarily spiking memory!
        setDownload(false);
      } catch (e) {} //error handling }
    }

    if (download) {
      downloadApi();
    }
  }, [download]);

  return <button onClick={() => setDownload(true)}>Download Private</button>;
}


For Public Files

import { useState, useEffect } from "react";
export default function DownloadPublicFile(props) {
  const [download, setDownload] = useState(false);

  useEffect(() => {
    if (download) {
      const link = document.createElement("a");
      link.href =
        "http://localhost:9000/api/v1/service/email/attachment/dummy.pdf";
      link.setAttribute("download", "dummy.pdf");
      link.click();
      setDownload(false);
    }
  }, [download]);

  return <button onClick={() => setDownload(true)}>Download Public</button>;
}

Good to know:

  1. Always control file downloads from server.

  2. Axios in the browser uses XHR under the hood, in which streaming of responses is not supported.

  3. Use onDownloadProgress method from Axios to implement progress bar.

  4. Chunked responses from server do not ( cannot ) indicate Content-Length. Hence you need some way of knowing the response size if you are using them while building a progress bar.

  5. <a> tag links can only make GET HTTP requests without any ability to send headers or cookies to the server (ideal for downloading from public endpoints)

  6. Brower request is slightly different from XHR request made in code.

Ref: Difference between AJAX request and a regular browser request

Jiles answered 15/8, 2021 at 19:27 Comment(0)
P
5

The trick is to make an invisible anchor tag in the render() and add a React ref allowing to trigger a click once we have the axios response:

class Example extends Component {
    state = {
        ref: React.createRef()
    }

    exportCSV = () => {
        axios.get(
            '/app/export'
        ).then(response => {
            let blob = new Blob([response.data], {type: 'application/octet-stream'})
            let ref = this.state.ref
            ref.current.href = URL.createObjectURL(blob)
            ref.current.download = 'data.csv'
            ref.current.click()
        })
    }

    render(){
        return(
            <div>
                <a style={{display: 'none'}} href='empty' ref={this.state.ref}>ref</a>
                <button onClick={this.exportCSV}>Export CSV</button>
            </div>
        )
    }
}

Here is the documentation: https://reactjs.org/docs/refs-and-the-dom.html. You can find a similar idea here: https://thewebtier.com/snippets/download-files-with-axios/.

Phio answered 25/10, 2018 at 13:20 Comment(0)
C
4

File download with custom header request. In this example, it shows how to send file download request with the bearer token. Good for downloadable content with authorization.

    download(urlHere) {
 
    axios.get(urlHere, {
      headers: {
        "Access-Control-Allow-Origin": "*",
        Authorization: `Bearer ${sessionStorage.getItem("auth-token")}`,
      }
    }).then((response) => {
    const temp = window.URL.createObjectURL(new Blob([response.data]));
    const link = document.createElement('a');
    link.href = temp;
    link.setAttribute('download', 'file.csv'); //or any other extension
    document.body.appendChild(link);
    link.click();
});
  }
Crocoite answered 29/9, 2021 at 6:42 Comment(1)
this works perfectly on my end.. does appending the a in my site doesnt affect perforamance?Barograph
L
2

You need to return File({file_to_download}, "application/vnd.ms-excel") from your backend to the frontend and in your js file you need to update the code that is written below:

function exportToExcel() {
        
        axios.post({path to call your controller}, null,
            {
                headers:
                {
                    'Content-Disposition': "attachment; filename=XYZ.xlsx",
                    'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
                },
                responseType: 'arraybuffer',
            }
        ).then((r) => {
            const path= window.URL.createObjectURL(new Blob([r.data]));
            const link = document.createElement('a');
            link.href = path;
            link.setAttribute('download', 'XYZ.xlsx');
            document.body.appendChild(link);
            link.click();
        }).catch((error) => console.log(error));
    }
Lupitalupo answered 6/6, 2021 at 21:31 Comment(0)
M
2

For those who'd like to implement an authenticated native download.

I'm currently developing a SPA with Axios.
Unfortunately Axios does't allow stream response type in such case. From documentation:

// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'

But I figured out a workaround as mentioned in this topic.
The trick is to send a basic Form POST containing your token and the targeted file.

"That targets a new window. Once the browser reads the attachment header on the server response, it will close the new tab and begin the download."

Here's a sample:

let form = document.createElement('form');
form.method = 'post';
form.target = '_blank';

form.action = `${API_URL}/${targetedResource}`;
form.innerHTML = `'<input type="hidden" name="jwtToken" value="${jwtToken}">'`;

document.body.appendChild(form);
form.submit();
document.body.removeChild(form);

"You may need to mark your handler as unauthenticated/anonymous so that you can manually validate the JWT to ensure proper authorization."

Which results for my ASP.NET implementation in:

[AllowAnonymous]
[HttpPost("{targetedResource}")]
public async Task<IActionResult> GetFile(string targetedResource, [FromForm] string jwtToken)
{
    var jsonWebTokenHandler = new JsonWebTokenHandler();

    var validationParameters = new TokenValidationParameters()
    {
        // Your token validation parameters here
    };

    var tokenValidationResult = jsonWebTokenHandler.ValidateToken(jwtToken, validationParameters);

    if (!tokenValidationResult.IsValid)
    {
        return Unauthorized();
    }

    // Your file upload implementation here
}
Materials answered 29/9, 2021 at 9:56 Comment(0)
G
2

Basically, I solved the problem of the filename by reading it, if present, from the 'content-disposition' header:

const generateFile = async ({ api, url, payload }) => {
    return await api({
        url: url,
        method: 'POST',
        data: payload, // payload
        responseType: 'blob'
    }).catch((e) => {
        throw e;
    });
};

const getFileName = (fileBlob, defaultFileName) => {
    const contentDisposition = fileBlob.headers.get('content-disposition');
    if (contentDisposition) {
        const fileNameIdentifier = 'filename=';
        const filenamePosition = contentDisposition.indexOf(fileNameIdentifier);
        if (~filenamePosition) {
            return contentDisposition.slice(filenamePosition + fileNameIdentifier.length, contentDisposition.length).replace(/"/g,'');
        }
    }
    return defaultFileName;
};

const downloadFile = (fileBlob, fileName) => {
    const url = window.URL.createObjectURL(new Blob([fileBlob]));
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', `${fileName}`);
    document.body.appendChild(link);
    link.click();
    link.remove();
    link.style.display = 'none';
    window.URL.revokeObjectURL(url);
};

// "api" is an instance of Axios (axios.create)
// "payload" is the payload you submit to the server
const fileBlob = await generateFile({ api, '/url/to/download', payload });
const fileName = getFileName(fileBlob, "MyDownload.xls");
downloadFile(fileBlob.data, fileName);
Gui answered 21/2, 2023 at 14:29 Comment(0)
K
1

This Worked for me. i implemented this solution in reactJS

const requestOptions = {`enter code here`
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};

fetch(`${url}`, requestOptions)
.then((res) => {
    return res.blob();
})
.then((blob) => {
    const href = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = href;
    link.setAttribute('download', 'config.json'); //or any other extension
    document.body.appendChild(link);
    link.click();
})
.catch((err) => {
    return Promise.reject({ Error: 'Something Went Wrong', err });
})
Kizer answered 7/1, 2021 at 8:42 Comment(0)
S
1

I had an issue where transferring one file I downloaded from axios const axiosResponse = await axios.get(pdf.url) to google drive googleDrive.files.create({media: {body: axiosResponse.data, mimeType}, requestBody: {name: fileName, parents: [parentFolder], mimeType}, auth: jwtClient}) uploaded a corrupted file.

The reason the file was corrupted was because axios transformed the axiosResponse.data to a string. To solve the issue, I had to ask axios to return a stream axios.get(pdf.url, { responseType: 'stream' }).

Sheehan answered 7/7, 2022 at 8:46 Comment(0)
M
0

Implement an Axios handler for the Received document, the data format octect-stream, data might look weird PK something JbxfFGvddvbdfbVVH34365436fdkln as its octet stream format, you might end up creating file with this data might be corrupt, {responseType: 'blob'} will make data into readable format,

axios.get("URL", {responseType: 'blob'})
     .then((r) => {
         let fileName =  r.headers['content-disposition'].split('filename=')[1];
         let blob = new Blob([r.data]);
         window.saveAs(blob, fileName);             
      }).catch(err => {
        console.log(err);
      });

you might have tried solution which fails like this, window.saveAs(blob, 'file.zip') will try to save file as zip but will wont work,

const downloadFile = (fileData) => {
    axios.get(baseUrl+"/file/download/"+fileData.id)
        .then((response) => {
            console.log(response.data);
            const blob = new Blob([response.data], {type: response.headers['content-type'], encoding:'UTF-8'});
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);
            link.download = 'file.zip';
            link.click();
        })
        .catch((err) => console.log(err))
}
const downloadFile = (fileData) => {
axios.get(baseUrl+"/file/download/"+fileData.id)
    .then((response) => {
        console.log(response);
        //const binaryString = window.atob(response.data)
        //const bytes = new Uint8Array(response.data)
        //const arrBuff = bytes.map((byte, i) => response.data.charCodeAt(i));
        //var base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(response.data)));
        const blob = new Blob([response.data], {type:"application/octet-stream"});
        window.saveAs(blob, 'file.zip')
        // const link = document.createElement('a');
        // link.href = window.URL.createObjectURL(blob);
        // link.download = 'file.zip';
        // link.click();
    })
    .catch((err) => console.log(err))
}
function base64ToArrayBuffer(base64) {
    var binaryString = window.atob(base64);
    var binaryLen = binaryString.length;
    var bytes = new Uint8Array(binaryLen);
    for (var i = 0; i < binaryLen; i++) {
        var ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    };

    return bytes;
}

another short solution is,

window.open("URL")

will keep opening new tabs unnecessarily and user might have to make allow popups for work this code, what if user want to download multiple files at the same time so go with solution first or if not try for other solutions also

Manakin answered 28/5, 2021 at 14:20 Comment(0)
S
0

This function will help you to download a ready xlsx, csv etc file download. I just send a ready xlsx static file from backend and it in react.

const downloadFabricFormat = async () => {
    try{
      await axios({
        url: '/api/fabric/fabric_excel_format/',
        method: 'GET',
        responseType: 'blob',
      }).then((response) => {
         const url = window.URL.createObjectURL(new Blob([response.data]));
         const link = document.createElement('a');
         link.href = url;
         link.setAttribute('download', 'Fabric Excel Format.xlsx');
         document.body.appendChild(link);
         link.click();
      });
    } catch(error){
      console.log(error)
    }
  };
Score answered 26/6, 2021 at 4:7 Comment(0)
I
-1

For axios POST request, the request should be something like this: The key here is that the responseType and header fields must be in the 3rd parameter of Post. The 2nd parameter is the application parameters.

export const requestDownloadReport = (requestParams) => async dispatch => { 
  let response = null;
  try {
    response = await frontEndApi.post('createPdf', {
      requestParams: requestParams,
    },
    {
      responseType: 'arraybuffer', // important...because we need to convert it to a blob. If we don't specify this, response.data will be the raw data. It cannot be converted to blob directly.
      headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/pdf'
      }
  });          
  }
  catch(err) {
    console.log('[requestDownloadReport][ERROR]', err);
    return err
  }

  return response;
}
Instantly answered 18/3, 2020 at 8:30 Comment(0)
U
-2

The answers using URL.CreateObject() have worked well for me. I still want to point out the option of using HTTP Headers.

Using HttpHeaders has these advantages:

  • very widespread browser support
  • does not require creating a blob object in the browser's memory
  • does not require waiting for the full response from the server before showing giving the user feedback
  • no size limitations

Using HttpHeaders requires you to have access to the back-end server where the files are downloaded from (which seems to be the case for OP's Excel files)

HttpHeaders solution:

FRONT-END:

//... 
// the download link
<a href="download/destination?parameter1=foo&param2=bar"> 
click me to download!
</a>

BACK-END

(C# in this example, but could be any language. Adapt as required)

...
var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read);
Response.Headers["Content-Disposition"] = "attachment; filename=someName.txt";
return File(fs, "application/octet-stream");
...

This solution assumes you have control of the back-end server that responds.

https://github.com/eligrey/FileSaver.js/wiki/Saving-a-remote-file#using-http-header

Uncontrollable answered 13/10, 2022 at 19:14 Comment(0)
O
-3

My answer is a total hack- I just created a link that looks like a button and add the URL to that.

<a class="el-button"
  style="color: white; background-color: #58B7FF;"
  :href="<YOUR URL ENDPOINT HERE>"
  :download="<FILE NAME NERE>">
<i class="fa fa-file-excel-o"></i>&nbsp;Excel
</a>

I'm using the excellent VueJs hence the odd anotations, however, this solution is framework agnostic. The idea would work for any HTML based design.

Oppose answered 22/11, 2017 at 5:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.