MustachedNinja
MustachedNinja

Reputation: 117

How to pipe a pdf download response from an api (node/express) to a client (react)?

In my specific setup, I have a backend server which generates a pdf, and then when a certain endpoint is visited, the pdf download is triggered. However, due to security rules, I cannot access that endpoint from my frontend, so I need to use a middle-man server to make the request to the backend, and forward it to my client.

Code:

Frontend (React):

const axios = require('axios');
...
function getPDFDownload() {
    let opts = {
        url: `${middleman_root}/download`,
        method: "GET"
    };
    return axios(opts)
        .then((result) => {
            console.log(result);
            return result;
        }).catch((error) => {
            console.log(error);
            return error;
        })
}

Middle-man server (Node/express):

const request = require('request');
...
router.get("/download", (req, res) => {
    res.set("Content-Type", "application/pdf");
    res.set("Content-Disposition", "attachment; filename:file.pdf");
    let url = `${backend_root}/download`;
    request.get(url).pipe(res);
})

Backend (Node/express):

router.get('/download', download);
...
const download = (req, res) => {
    ...
    return generatePDF()
        .then(({ filepath, filename }) => {
            res.download(filepath, filename);
        })
}

The frontend function getPDFDownload() is called when a button is clicked, which then sends a request to the middleman server, which should then send a request to the backend server, and pipe the response back to the frontend.

Whats currently happening:

It seems that either some or all of the pdf is being sent, since the console.log(result) is printing the following data:

{
    "data": "%PDF-1.4↵%����↵1 0 obj↵<<↵/Type /Catalog↵/Version...",
    "headers": {
        "content-disposition": "attachment; filename=\"file.pdf\"",
        "content-type": "application/pdf",
        "content-length": "648748"
    },
    "status": 200
    ...
}

However, the file is not being downloaded. I know that there exists a library called downloadjs which helps with these problems, but I would like to solve this without adding another library since I don't need to download files often.

What I've tried:

fs.readFile(filepath, (err, data) => {
    if (err) throw err;
    const pdf = data.toString('base64');
    res.write(pdf, 'base64');
    // Since res.write would produce no result, I tried adding res.send() after res.write(), 
    // which achieved the same result, of pdf data being delivered, but not downloaded

    // Also tried replacing res.write(pdf, 'base64') with res.write(data, 'base64');
});

Frontend code (modified):

import download from 'downloadjs';
...
function getPDFDownload() {
    ...
    return axios(opts)
        .then((result) => {
            download(result.data, "file.pdf", result.headers['content-type']);
        })
        .catch((error) => {
            console.log(error);
        })
}

Upvotes: 2

Views: 2803

Answers (1)

MustachedNinja
MustachedNinja

Reputation: 117

I finally got it to work, after reluctantly installing downloadjs Basically I changed my frontend code to look like this:

import axios from 'axios';
import download from 'download';
...
function getPDFDownload() {
    let opts = {
        url: `${middleman_root}/download`,
        method: "GET",
        responseType: 'blob'
    };
    return axios(opts)
        .then((result) => {
            download(result.data, "file.pdf", result.headers['content-type']);
        }).catch((error) => {
            console.log(error);
        })
}

Upvotes: 2

Related Questions