Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download file from Express API using React and Axios

When using a React client with an Express API, how can the React client download a file that was send by the Express API?

Problem:

  • If I type the url into my browser bar and press enter the file downloads successfully.
  • But if I call the same url in my React app using Axios, the file does not download.

Express server

// Route handler for /api/files/testfile
const getFile = async (req, res, next) => {

    // File
    const fileName = 'file.csv';
    const filePath = path.join(__dirname, '/../../public/', fileName);

    // File options
     const options = {
        headers: {
            'x-timestamp': Date.now(),
            'x-sent': true,
            'content-disposition': "attachment; filename=" + fileName, // gets ignored
            'content-type': "text/csv"
        }
    }

    try {
        res.download(
            filePath,
            fileName,
            options
        );
        console.log("File sent successfully!");
    }
    catch (error) {
        console.error("File could not be sent!");
        next(error);
    }
});

React client

// When the user clicks the "Download as CSV" button
handleDownloadFile = () => {
    axios
        .get(
            `/api/files/testfile`, {
                responseType: 'blob',
                headers: {
                    'Content-Type': 'text/csv',
                }
            }
        )
        .then(response => {
            console.log(response.headers); // does not include content-disposition
            console.log("File downloading successfully!");
        })
        .catch( (error) => {
            console.error("File could not be downloaded:", error);
        });
}

I read that this might have to do with the content-disposition header. I tried setting in (see my above code) but the header does not get send to the client.


Undesirable "solutions":

  • In the React app: Create a new a element, set its href attribute and trigger a click via JavaScript. I am looking for a solution that does not require this JS hack.

  • In the React app: Use a with target="_blank" instead of Axios. However, that is not suitable for me, as it would bypass my axios config settings (API url, auth token, etc)

like image 899
Ben Avatar asked May 18 '26 09:05

Ben


1 Answers

It seems like you have to tell axios where the file is directly based on this example:

axios({
  url: 'http://localhost:5000/static/example.pdf',
  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');
  document.body.appendChild(link);
  link.click();
});

I would assume you can simply change the response on your api to return the blob using a new Blob for the file. But the main part of what it seems to require is the .then response on your axios get call. This way you can still authenticate users' status with jwt and protect your files appropriately.

like image 82
Atlante Avila Avatar answered May 19 '26 21:05

Atlante Avila