1

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

5
  • How big is the file you want to download? Commented Oct 27, 2018 at 20:36
  • 178Kb, not a big one. For bigger ones I tried creating an stream and piping the stream into the response. Same problem. Commented Oct 27, 2018 at 20:40
  • I added an answer with an alternative, have a look and let me know if it works Commented Oct 28, 2018 at 14:59
  • Still facing same issue. Do you have a working example with a small pdf file? Are you able to fetch it from the server, send the file to the browser, download it and open it normally? For me only works when I put the file in the public folder and reference it directly. but this should not be the solution in my opinion Commented Oct 31, 2018 at 12:01
  • @fgonzales yes, I added a fiddle showing the basic download of the file. For your case of getting the file from the server, just encode it in base64, return that value (the complete string), and replace the variable text in my answer with that value. Commented Oct 31, 2018 at 12:26

3 Answers 3

5

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

Sign up to request clarification or add additional context in comments.

Comments

0

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.

2 Comments

It should be possible to have an endpoint to send a file from server to client, no need to place all files in public folder. Because what about if I want to make the file available only for some authenticated users?
use an specific path for requested files and provide your choosed authentication model before use the static function inside the callback
0

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.

1 Comment

This "CORS" error is because of the server response headers. I have my own server and I set the "Access-Control-Allow-Origin: *" in the response so I do not experience this problem. In other servers it could cause a problem I guess. You can find a huge amount of discussions here in Stackoverflow, but the best is if you have your own server and have control over the response object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.