0

Background

I am generating a PDF on a express server and saving it to a local directory. In the callback I am attempting to send the PDF back to the server so it will download but it appears i the network tab response as what appears to be binary and does not download.

Example

EDIT My Client Side Function Makes Server Request This fetch call hits an express end point that should return a PDF. The express route is below,

function setImage() {
  html2canvas(document.querySelector('#chart')).then(canvas => {
    canvas.toBlob(
      function(blob) {
        const url = 'http://localhost:3000/api/v1/pdf';

        fetch(url, {
          method: 'POST',
          headers: {
            'Content-type': 'application/octet-stream',
          },
          body: blob,
        })
          .then(response => response.json())
          .then(success => console.log(success))
          .catch(error => console.log(error));
      },
      'image/png',
      1,
    );
    document.body.appendChild(canvas);
  });
}

// Creates PDF and saves it on server at filePath

router.post('/', (req, res, next) => {
      pdf.create(html, options).toFile(filePath, err => {

            if (err) {
              return console.log(err);
            }
            const stat = fs.statSync('./downloads/report.pdf');
            res.contentType('application/pdf');
            res.setHeader('Content-Length', stat.size);
            res.setHeader('Content-Type', 'application/pdf');
            res.setHeader('Content-Disposition', 'attachment; filename=report.pdf');
        // *Should force download the file to the browser*
            res.download('../downloads/report.pdf', 'report.pdf', function(e) {
              if (e) {
                // Handle error, but keep in mind the response may be partially-sent
                // so check res.headersSent
              } else {
                // It worked, do something else.
              }
            });
          });
    });

EDIT I also tried this on the server it shows the response in the browser but will not download a file.

router.post('/', (req, res, next) => {
  const img = req.body;
  const filepath = 'uploads/chart.png';
  fs.writeFile(filepath, img, err => {
    if (err) {
      throw err;
    }
    console.log('The file was succesfully saved!');
  });

  const html = tmpl.replace('{{chart}}', `file://${require.resolve('../uploads/chart.png')}`);
  const options = { format: 'Letter' };
  const fileName = 'report.pdf';
  const filePath = './downloads/report.pdf';

  pdf.create(html, options).toFile(filePath, err => {
    if (err) {
      return console.log(err);
    }
    const stat = fs.statSync('./downloads/report.pdf');
    res.setHeader('Content-Description', 'File Transfer');
    res.setHeader('Content-Length', stat.size);
    res.setHeader('Content-type', 'application/octet-stream');
    res.setHeader('Content-type', 'application/pdf');
    res.setHeader('Content-Type', 'application/force-download');
    res.setHeader('Content-disposition', 'attachment;filename=report.pdf');

    res.sendFile('/downloads/report.pdf');
  });

Example of output in browser

enter image description here

Question

What is the proper way in Express 4 to force a browser to download a PDF?

wuno
  • 9,547
  • 19
  • 96
  • 180
  • I tested your code and it seems to work fine on my example. It has more to do with how the browser chooses to handle this mime type, where is the link to the "download" placed? can you share that code? – in need of help May 10 '18 at 06:45

1 Answers1

1

I see your issue now, fetch does a request in the background using AJAX, the response goes to your code and you catch it and write it to the screen as a JSON. In order to open a file dialogue and download the file you either need to have a link that points to the same location with <a href=....> or you could try something that is suggested here How can I download a file using window.fetch?

in need of help
  • 1,606
  • 14
  • 27