Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pull PDF from Amazon S3 and render directly in Browser from buffer/stream

I have a requirement where PDFs aren't kept on a local server but stored privately on the Amazon S3 bucket. But upon specific requests, I need to retrieve the PDF and render it directly in the user's browser without downloading it on the web server.

I am able to pull the stream fine in node.js and respond with the PDF data. When I inspect the response data in the Chrome dev tools, it looks like the PDF data, and I can even read the text in the document.

let params = {Bucket: process.env.S3STORAGE, Key: req.query.fileName};
res.attachment(req.query.fileName);
s3.getObject(params).createReadStream().pipe(res);

And I've tried multiple things to render it correctly. It is either displaying a blank PDF, or the gobbly gook PDF data.

I am using a vue2 template, and trying to pop the PDF in a bootstrap modal. In this attempt, I get a blank PDF:

<object :data="pdfStream" type="application/pdf" width="800px" :height="browserHeight"></object>


this.pdfStream = response.bodyText;

I saw some answers that said base64 encode it, but still not getting that to work either.

// server side
let params = {Bucket: process.env.S3STORAGE, Key: req.query.fileName};
s3.getObject(params).createReadStream().pipe(strs('binary', 'base64')).pipe(res);

Then

//client side
 let objbuilder = '';
        objbuilder += ('<object width="100%" height="100%" data="data:application/pdf;base64,');
        objbuilder += (response.bodyText);
        objbuilder += ('" type="application/pdf" class="internal">');
        objbuilder += ('<embed src="data:application/pdf;base64,');
        objbuilder += (response.bodyText);
        objbuilder += ('" type="application/pdf"  />');
        objbuilder += ('</object>');
        this.pdfStream = objbuilder;

Surely I am missing something simple, or making a dumb mistake, but not sure what it is. I have always just served up actual files, but in this specific instance, the goal is to just render the data directly into the browser without generating a copy of the PDF sitting in S3 storage.

UPDATE

Jason's answer is pointing me in the right direction, and I believe I am now on the right track. However it's throwing errors I am unsure of.

My first attempt is this:

// returning a base64 encoded PDF from Amazon to the client
let params = {Bucket: process.env.S3STORAGE, Key: req.query.fileName};
s3.getObject(params).createReadStream().pipe(strs('binary', 'base64')).pipe(res);

Then in the client side Vue component:

//import PDFJS from 'pdfjs-dist';
export default {
  {components: PDFJS},
  ...
  ...

  ...
  viewDocument(fileName, documentName) {

      this.$http.get('/fetchDocument', {
        params: {
          fileName: fileName
        }
      })
      .then(response => {

         PDFJS.getDocument(response.bodyText).then(function (pdfDocument) {

          console.log('Number of pages: ' + pdfDocument.numPages);

        });
      });

    },

And it shoots back this error:

app.js:58668 GET http://192.168.3.14:3000/dist/app.worker.js net::ERR_ABORTED
app.js:55073 Warning: Setting up fake worker.
app.js:104 GET http://192.168.3.14:3000/0.js net::ERR_ABORTED
app.js:99 Uncaught (in promise) Error: Loading chunk 0 failed.
    at HTMLScriptElement.onScriptComplete (app.js:99)

There is no app.worker.js or 0.js in the pdfjs-dist library.

Then...if I return from node the original way without base64 and it returns the raw PDF stream that starts like:

%PDF-1.2
%����
3 0 obj
<< 
/Lineariz

I get an entirely different error

Uncaught (in promise) TypeError: Failed to construct 'URL': Invalid URL

Finally, I tried the base64Uint8Array function from here on the base64 encoded data returned from node, which led to the app.worker.js and 0.js 404 errors I just received.

The pdfjs-dist package does not have those js files, so not sure what is exactly happening.

Any thoughts?

like image 744
Nathan Avatar asked Dec 12 '17 22:12

Nathan


Video Answer


1 Answers

You need to use a renderer to render the PDF data into the browser. One such project is Mozilla's PDF.JS project. Since you're using vue, you can look at this example for usage: https://github.com/shershen08/vue2-pdfjs-viewer

Browsers already have PDF readers built into them, but not in the context of a JS application. That's where the PDF.JS project comes into the picture.

like image 138
Jason Avatar answered Oct 25 '22 08:10

Jason