1

I would like to be able to send pdf files with nodejs to the frontend. But when I do this, I get an error and I can't open the file. This is the error (translation of error: an error occurred when loading the PDF document): enter image description here

I think that all is well but still without working.

Here is the nodeJS code:

routerTrainer.get("/download-training", verifyJWT, async (req, res) => {

  const { training_id } = req.headers;

  let training = await Training.findOne({
    where: { id: training_id },
  });

  if (training) {
   res.download(`${path}${dirname}${training.file_id}`);
  }
});

And here is the React frontend code:

const downloadTraining = async (id) => {
    const JWT = new ClassJWT();
    const axiosReq = axios.create();
    await JWT.checkJWT();
    axiosReq
      .get(`${serverPath}/download-training`, {
        headers: {
          training_id: id,
          token: JWT.getToken(),
          responseType: "blob"
        },
      })
      .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();
      })
      .catch((err) => console.log(err));
  };

Don`t worry about all that have JWT like verifyJWT or ClassJWT, this are implementations of json web tokens and it works correctly.

If anyone know how to fix it, please let me know.

Gonsa02
  • 333
  • 3
  • 13

2 Answers2

2

you have to convert the binary file to a blob (in this example is set the responseType of xhr as blob), then convert it to base64 encoded file, here is an example:

<html>
  <body>
    <h1><a>dl</a></h1>

    <script>
      const pdfSrc = "https://blahblah.com/e-book.pdf";
      const linkTag = document.querySelector("a");
      const xhr = new XMLHttpRequest();
      const fileReader = new FileReader();

      xhr.open("GET", pdfSrc);
      xhr.responseType = "blob";

      xhr.addEventListener("loadend", () => {
        fileReader.readAsDataURL(xhr.response);
      });

      fileReader.addEventListener("loadend", (event) => {
        const base64File = event.srcElement.result;
        linkTag.href = base64File;
        linkTag.setAttribute("download", "file.pdf");
      });
      xhr.send();
    </script>
  </body>
</html>
PS-PARSA
  • 932
  • 5
  • 15
  • Sorry for my ignorance but I don't know how to apply your code to mine, I find it confusing that you don't use axios and I do, would you mind doing the example with my code? Sorry for the inconvenience. – Gonsa02 Sep 04 '21 at 17:37
  • 1
    @Gonsa02 why not, take a look at this: https://pastebin.com/eiXid3GL – PS-PARSA Sep 04 '21 at 18:38
  • Thanks for the code but when I execute the function I get this console error: TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'. I have used res.download() maybe that's not a blob, how can I send a blob? – Gonsa02 Sep 04 '21 at 19:27
  • i used the download method too, i had no problem with that; maybe you should post a new question about this error... – PS-PARSA Sep 04 '21 at 20:01
  • I've done fileReader.readAsDataURL(new Blob([res.data])) and now it is downloading but I get the same error with the pdf downloaded: an error occurred when loading the PDF document – Gonsa02 Sep 04 '21 at 20:24
  • @Gonsa02 looks your response is not a valid pdf file. – PS-PARSA Sep 04 '21 at 20:35
0

In my case, in back-end (ExpressJs) I have something like -

app.get('/api/v1/getPdf', function (req, res) {
  let resolve = require('path').resolve;
  res.download(resolve('./folder/file.pdf'));
});

and in ReactJS (without TypeScrypt), I'm using native fetch instead of axios:

  const onButtonClick = async () => {

let file = null;
await (async () => {
  const rawResponse = await fetch('http://<host:port>/api/v1/getPdf', {
    method: 'get',
    headers: {
      'Content-Type': 'application/json',
    },
  });
  file = await rawResponse.blob();
})();

const pdfWindow = window.open();
pdfWindow.location.href = window.URL.createObjectURL(file);
};

where in component

return (
<>
  <center>
    <h1>Welcome</h1>
    <h3>Click on below button to download PDF file</h3>
    <button onClick={onButtonClick}>Download PDF</button>
  </center>
</> 
);

and it opens in the new window pdf file like (see link) enter image description here

if you want to download file, you must implemente onButtonClick a little bit different

const onButtonClick = async () => {

let file = null;
await (async () => {
  const rawResponse = await fetch('http://<host:port>/api/v1/getPdf', {
    method: 'get',
    headers: {
      'Content-Type': 'application/json',
    },
  });
  file = await rawResponse.blob();
})();

// const pdfWindow = window.open();
// pdfWindow.location.href = window.URL.createObjectURL(file);
const a = document.createElement('a');
a.href = window.URL.createObjectURL(file);
a.download = 'file.pdf';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

On my side it works like a charm...

GoAl
  • 1
  • 1