fgonzalez
fgonzalez

Reputation: 3877

Cannot open pdf file downloaded from server using Express and Node Js

I am trying to send a pdf file hosted on the server to the client, to be downloaded from the browser. I am using express and node js.

The code on the server is :

app.get('/files', async (req, res) => {
     res.sendFile(__dirname + '/boarding-pass.pdf');
    });

The code on the client (react js) is :

const handleClick = async () => {
    const response = await axios({
        url: 'http://localhost:4000/files',
       // url: '/static/boarding-pass.pdf',
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/pdf',
            'Authorization': 'Basic d29vZG1hYzpXb29kbWFjOTI3IQ=='
        },
        responseType: 'arraybuffer',
        //responseType: 'blob', // important
    });

console.log('response', response);
    const url = window.URL.createObjectURL(new Blob([response.data]));
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', 'bp.pdf');
    document.body.appendChild(link);
    link.click();
}

export default () => <div><Button onClick={() => handleClick()}>Download file</Button></div>

If I try to open the file on the server (I am on a Mac), the file is opened correctly and I see the content. However when I download the file from the browser, it gets someway corrupted or truncated, or it is missing something, because I can not open it, and I am getting the message that it is not a valid file, although I can see that the size of both files in the file system is the same, but if I inspect the binaries with an utility, I can see both files are different..

Can someone tell me what I am missing or provide an small working example?

Thank you

Upvotes: 1

Views: 5001

Answers (3)

Solution is much easier then we thought thanks for Edo_M

The key is res => res.blob() this will make the job for you.

My backend representation (Nodejs + express):

...
module.exports=function(app){
    // GET download specified pdf file
    app.get('/getpdf/:uuid', middlewarefunc.authenticateToken, (req, res) => {
    
        if(!req.params.uuid) return res.status(400).json({error:"Missing UUID param!"});
        
        res.setHeader('Content-Type', 'application/pdf');
        
        res.setHeader("Content-Disposition", "attachment");
        
        res.download(`./${req.params.uuid}`, (err)=>{
            console.log("Error: ", err);
        });
    });
}
...

My frontend solution (tested in chrome):

...
function handleDownload(e, uuid){
  e.preventDefault();
    fetch(`${process.env.REACT_APP_SERVER}/getpdf/${uuid}`,
      {
        method: 'GET',
        headers: {
          'Accept': 'application/pdf',
          'Authorization': 'Bearer ' + session.accessToken,
          'Content-Type' : 'application/json'
        }
      })
      .then(res=>res.blob())
      .then(response => {
        var fileURL = URL.createObjectURL(response);
        window.open(fileURL);
      })
      .catch(err => {
        console.error(err);
        alert(err.error);
      });
...

// place a button to anywhere with onClick:
<button  onClick={(e) => handleDownload(e, pdf_uuid)}>Download PDF</button>

...

You will get a new tab/window in the browser with the requested PDF content.

Upvotes: 0

c-chavez
c-chavez

Reputation: 7496

You can try an alternative to using a Blob.

Set the data type in the href of your link:

link.setAttribute('href', 'data:application/pdf;base64,' + text);

or

link.setAttribute('href', 'data:application/octet-stream;base64,' + text);

or if you are still getting a corrupted file, encode your content:

link.setAttribute('href', 'data:application/pdf;charset=utf-8,' + encodeURIComponent(text));

If it's text I always use:

link.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));

And don't forget to remove the dom object after downloading the file:

document.body.removeChild(link);

This is the complete code:

let link= document.createElement('a');
link.setAttribute('href', ''data:application/pdf;base64,' + text);
link.setAttribute('download', 'bp.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // Remember to remove the dom object after downloading the file

This is a fiddle showing this functionality with a base64 encoded pdf:

Fiddle

Upvotes: 5

Ivo Ribeiro
Ivo Ribeiro

Reputation: 1

I think you should change the server implementation and do something like below:

var express = require('express'); var app = express();

app.use(express.static('public')); app.use(express.static('images'));

app.listen(3000);

And then request host:3000/bp.pdf ,the file should be in the public,images folder on the root of your node application.

Upvotes: 0

Related Questions