user3527354 user3527354 - 6 months ago 93
Node.js Question

Trying to merge pdf files in node: PDFUnite + Nodejs

I have a site on heroku, and am using the HyPDF addon which allows you to manipulate pdf's in a number ways. Specifically, I am trying to merge two pdf's that I have the URL's for.

However, it's not working. I am using request to pull the pdf files and then using request to make the call to PDFUnite. It's a bit of callback hell too, so if you have any suggestions on how to fix that, I would really appreciate it!

Thanks!

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=4010e80d-9106-4824-95ab-799ec81c7fd0', encoding: null}, function(err, res, body) {
var pdf1 = body;

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=28a388c6-ca0e-45a1-9aaf-9b6688c5a557', encoding: null}, function(err2, res2, body2){
var pdf2 = body2;

request.post(
'https://www.hypdf.com/htmltopdf',
{
json: {
user: HY_USER,
password: HY_PASS,
file_1: pdf1,
file_2: pdf2
}
},
function (error3, res3, body3) {

if (!error && res2.statusCode == 200) {
console.log('Public URL: ', body3.url);
console.log('Number of pages: ', res3.headers['hypdf-pages']);
}
});
})
});


/****************UDPATED*****************/

I've updated the code, using https://gist.github.com/redfield/6724717 as a guide. The difference is that code sample uses files, whereas I'm using a URL.

I've tried to modify it appropriately, but obviously something is off...If I use body or body.toString('base64') as the pdf1 and pdf2 then I get a 400 status error. Otherwise, if I use the res as the pdf files, then I get a 504 error. I'm not really sure how I should be sending in the files, so it's just guess and check. Appreciate the help!

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=4010e80d-9106-4824-95ab-799ec81c7fd0', encoding: null}, function(err, res, body) {
var pdf1 = body;

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=28a388c6-ca0e-45a1-9aaf-9b6688c5a557', encoding: null}, function(err2, res2, body2){
var pdf2 = body2;

var form = new FormData();
form.append('user', HYPDF_USER);
form.append('password', HYPDF_PASSWORD);
form.append('test', 'true');
// form.append('bucket', 'hypdf_test');
form.append('key', 'hypdf_test.pdf');
form.append('public', 'true');
form.append('file1', pdf1);
form.append('file2', pdf2);

form.submit('https://www.hypdf.com/pdfunite', function(err, res) {
console.log('err ', err);
// res – response object (http.IncomingMessage)
console.log(res.statusCode, res.resume());
});
})
});


UPDATE #2
I've updated the code combining my code with @remus' code below. However, I'm still getting an error - "Cannot call method 'hasOwnProperty' of null" on the request.post line. Any thoughts?

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=4010e80d-9106-4824-95ab-799ec81c7fd0', encoding: null}, function(err, res, body) {
var pdf1 = body.toString('base64');

request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=28a388c6-ca0e-45a1-9aaf-9b6688c5a557', encoding: null}, function(err2, res2, body2){
var pdf2 = body2.toString('base64');

var form = new FormData();
form.append('user', HY_UN);
form.append('password', HY_PASS);
form.append('public', 'true');
form.append('file1', fs.createReadStream(pdf1));
form.append('file2', fs.createReadStream(pdf1));

request.post({
url: 'https://www.hypdf.com/pdfunite',
formData: form
}, function(e, r, body) {
// body should be the binary result of the merged .pdf
console.log('e', e);
console.log('body', body);
});

})
});


UPDATE 3

ERROR STACK for the request.post - I've tried a number of things, can't get rid of it. Any thoughts?

TypeError: Cannot call method 'hasOwnProperty' of null
at appendFormValue (/node_modules/request/request.js:340:17)
at Request.init (/node_modules/request/request.js:354:11)
at new Request (/node_modules/request/request.js:140:8)
at request (/node_modules/request/index.js:55:10)
at Function.post (/node_modules/request/index.js:63:12)
**at Request._callback (/server/api/emailInvoice/emailInvoice.controller.js:34:12)**
at Request.self.callback (/node_modules/request/request.js:198:22)
at Request.emit (events.js:98:17)
at Request.<anonymous> (/node_modules/request/request.js:1073:14)
at Request.emit (events.js:117:20)
at IncomingMessage.<anonymous> (/node_modules/request/request.js:1019:12)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:929:16
at process._tickCallback (node.js:419:13)

Answer

First off, the main problem is you're trying to POST binary data (.pdf) as JSON in your last POST request. I imagine that's not going to work - like maybe it expects multipart file data? Second, an easy way to fix up your callback hell is to use :

var request = require('request');
var async = require('async');

async.parallel(function(callback) {
    pdf1: function(callback) {
        request.get({
            url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=4010e80d-9106-4824-95ab-799ec81c7fd0'
        }, function(err, r, body) {
            callback(err, body);
        });
    },
    pdf2: function(callback) {
        request.get({
            url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=28a388c6-ca0e-45a1-9aaf-9b6688c5a557'
        }, function(err, r, body) {
            callback(err, body);
        });
    },
    function(err, results) {
        var formData = {
            user: HY_USER,
            password: HY_PASS,
            file_1: fs.createReadStream(results.pdf1),
            file_2: fs.createReadStream(results.pdf2)
        }
        request.post({
            url: 'https://www.hypdf.com/pdfunite',
            formData: formData
        }, function(e, r, body) {
            // body should be the binary result of the merged .pdf
        });
    });
});

Notice both the fs.createReadStream from the binary data stream, and the proper url using pdfunite.

You might also want to check out the hypdf NPM module - I didn't test it out, but it might make things even easier than manually building up the request.

****UPDATE****

The problem was with using FormData(). For some reason, request.post didn't like it. I was also able to remove the callbacks by injecting the request streams directly into the form. Also note, you have to set encoding to null.

    var finalpdf;
    var formData = {
        'user': HY_USER,
        'password': HY_PASSWORD,
        'test': 'true',
        'public': 'true',
        'file1': request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=4010e80d-9106-4824-95ab-799ec81c7fd0', encoding: null}),
        'file2': request({url: 'http://www.sesamestreet.org/cms_services/services?action=download&uid=28a388c6-ca0e-45a1-9aaf-9b6688c5a557', encoding: null})
    }


    request.post({url: 'https://www.hypdf.com/pdfunite', encoding: null,     formData: formData}, function(err, res3, body3){
       finalPdf = body3.toString('base64');
});