How to download files using axios

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?


Solution 1:

A more general solution

axios({
    url: 'http://api.dev/file-download', //your url
    method: 'GET',
    responseType: 'blob', // important
}).then((response) => {
    const url = window.URL.createObjectURL(new Blob([response.data]));
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', 'file.pdf'); //or any other extension
    document.body.appendChild(link);
    link.click();
});

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

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

Solution 2:

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

Hope this helps :)

Solution 3:

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

Solution 4:

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