Display Pdf in browser using express js

72,710

Solution 1

I tested your code and it works for me in chrome with one change: Change app.post to app.get

EDIT: since you seem to think a POST-only server is a good idea, read this: http://net.tutsplus.com/tutorials/other/a-beginners-introduction-to-http-and-rest/ Scroll down until the HTTP verbs and check out the difference between GET and POST. :)

Some quick research suggests that other browsers might have other issues, IE for example might expect the URL to end in .pdf. Since I'm on my Mac I can't test that for you ;)

Solution 2

Specifying how a file download is handled all comes down to the Content-disposition header. You can also specify the name of the file here as well. We also set the Content-type to ensure the browser knows what to do with the file given to it.

Express.js Example:

app.post('/url/to/hit', function(req, res, next) {
  var stream = fs.readStream('/location/of/pdf');
  var filename = "WhateverFilenameYouWant.pdf"; 
  // Be careful of special characters

  filename = encodeURIComponent(filename);
  // Ideally this should strip them

  res.setHeader('Content-disposition', 'inline; filename="' + filename + '"');
  res.setHeader('Content-type', 'application/pdf');

  stream.pipe(res);
});

Now if you look more closely at the Content-disposition, you'll notice the inline; field is what sets how the browser reacts to the file. If you want to force downloads, you can do so by setting inline; to attachment;

I've also found out (by being burnt a couple times), that if you set special characters in your filename, it can break. So I encodeURIComponent() the filename to ensure that doesn't happen.

Hope that helps others trying to figure out the same!

Edit

In the time between me posting this originally and now, I've found out how to correctly encode the content-disposition's filename parameter. According to the spec, the filename should be RFC5987 encoded. I ended up finding an example code snippet from MDN that correctly handles the encoding here (encodeURIComponent() isn't the entirely correct format for this field).

MDN Snippet

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

console.log(header); 
// logs "Content-Disposition: attachment; filename*=UTF-8''my%20file%282%29.txt"

function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves "!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987, 
            // so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);
}

Another note on top of this one, browsers don't fully comply with the spec either. Some characters will still come back incorrectly from a download (at least when I tested it).

You can get around this problem by updating how your downloads work. If your download URL ends with the filename (and you don't supply a filename attribute in the header), it will correctly get the filename from the URL encoded value. IE 'http://subdomain.domain-url.com/some/path/to/downloads/' + encodeURIComponent("You're there, download this!.pdf")

Jeeze, and all to supply a file name to your downloads!

Solution 3

Actually Express already has this feature for sending files. All you need is :

app.get('/sendMePDF', function(req, res) {
  res.sendFile(__dirname + "/static/pdf/Rabbi.pdf");
})

Here the server will send the file "Rabbi.pdf" and it will open in browser like you open pdf in browser. I placed the file in "static" folder but you can place it anywhere, knowing that sendFile() takes as argument the absolute path (not relative one).

Solution 4

My Solution for sending a PDF directly to the Browser:

app.get('/my/pdf', function (req, res) {
    var doc = new Pdf();
    doc.text("Hello World", 50, 50);

    doc.output( function(pdf) {
        res.type('application/pdf');
        res.end(pdf, 'binary');
    });
});

res.end() with the second param 'binary' did the trick in my case. Otherwise express interpret it as a string

Solution 5

It is as simple as following code:

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

app.get('/', function (req, res) {
    var filePath = "/files/my_pdf_file.pdf";

    fs.readFile(__dirname + filePath , function (err,data){
        res.contentType("application/pdf");
        res.send(data);
    });
});

app.listen(3000, function(){
    console.log('Listening on 3000');
});

For complete repo:

Clone node-cheat pdf_browser, run node app followed by npm install express.

Happy Helping!

Share:
72,710
Dexter
Author by

Dexter

Fine....

Updated on December 02, 2021

Comments

  • Dexter
    Dexter over 2 years

    I'm trying to serve a PDF via Express in a way it is displayed in browser:

    app.post('/asset', function(request, response){
      var tempFile="/home/applmgr/Desktop/123456.pdf";
      fs.readFile(tempFile, function (err,data){
         response.contentType("application/pdf");
         response.send(data);
      });
    });
    

    However, the browser shows binary content. How to handle this correctly?

  • Dexter
    Dexter almost 12 years
    yes after changing from post to get it is working in my firefox, safari, opera and chrome
  • Dexter
    Dexter almost 12 years
    So it is not possible with the app.post ? Because I configured the server and maintaining to take only post requests.
  • rdrey
    rdrey almost 12 years
    hmm... not sure why you would configure your server to do POST requests only: browser's GET any URL you send them to. That's just how http works. :) POST is usually used for form submissions, or when you use HTTP to create something, not GET it.
  • rdrey
    rdrey almost 12 years
    @DSK added a link in my answer that will clear this up for you.
  • AlbertEngelB
    AlbertEngelB over 10 years
    To be fair, there are use cases where you would want to get a PDF in response to a POST.
  • Tyler Eich
    Tyler Eich about 10 years
    The method doc.output is now deprecated.
  • user137717
    user137717 over 9 years
    I'm pretty new to Node. What makes this method of serving up a pdf so different from the sendfile(some.html) that I've been using. Can someone explain how this is working and why I don't have to read from the file system for html files?
  • user137717
    user137717 over 9 years
    @rdrey I get a weird warning from this. Firefox says this pdf may not be displayed properly, click here to display somewhere else. The pdf does not display. anyone know why that might be?
  • Ming
    Ming almost 9 years
    Thx! The 'binary' param really saved my day!
  • AlbertEngelB
    AlbertEngelB over 8 years
    @user137717 Probably the content-type header. It gives the browser a hint on how to open the incoming file.
  • rdrey
    rdrey over 8 years
    Yeah, I'd like it if @Dropped.on.Caprica 's comment was marked the accepted answer, it's clearer than mine.
  • AlbertEngelB
    AlbertEngelB over 8 years
    @rdrey Thanks for your vote of confidence! I expanded on my answer a bit as well, to cover the various issues I've found with setting filenames. Hopefully this will help someone else who's trying to supply a PDF download.
  • Marcus
    Marcus over 7 years
    Just pointing out a typo, it should be "attachment" in the content-disposition header, not "attatchment".
  • Gangula
    Gangula almost 3 years
    this open the pdf file in the same window. Is there a way to open it in a new window?
  • Ivan Lopes
    Ivan Lopes almost 3 years
    in some browsers it will simply ask you to download it
  • ReZ
    ReZ over 2 years
    Thanks very much, this answer really save my day with jsPDF