SSRS generated PDF returning empty when retrieved via Nodejs Express Server

Long time reader, first time poster.

I am trying to generate a PDF from SSRS and serve it to the end user with a NodeJS (Express) server acting as a service layer in the middle. We don't want to provide the user with anything that is SSRS related, so I have a server node that binds a link and makes a fetch request to the server.

The problem is that I can get the PDF through the response body from SSRS and pipe it to the HTML page, but the file is completely empty despite the correct page count and file size. This happens even when I write data to a file as a test.

When using POSTMAN or curl in a web browser, or making a direct GET request from Chrome, it successfully downloads the file as an attachment and no body. However, when I make the same get request via node, I get a header that asserts that there is an attachment along with a body containing a PDF that I can only imagine was converted to utf-8 encoding when it hit my node server. Since the report is generated via SSRS, I cannot use express response.download () or similar functionality because I don't have the exact path to the PDF before generating it.

Something more complicated, I have to rely on requests constrained in the http-ntlm library to get a response from our SSRS server to start with, so pipeline and similar methods are not available to me as far as I know. I chose this primarily because it allows me to access SSRS over http rather than https (our SSRS config doesn't respond to https).

Server node function:

exports.getReportPDF = function (req, resp)
{
//-Gets the link info from the client side
var myLink = req.body;
var fileLink = myLink.Data;

httpntlm.post({
    url: fileLink,
    username: 'username',
    password: 'password',
    workstation: 'workstation',
    domain: 'domain'
}, function (err, res){ //-Returns once the stream is complete
    if(err) return err;

    var resBase64 = new Buffer(res.body).toString('base64');
    resp.setHeader('Content-Type', 'application/octet-stream');
    resp.setHeader('Content-Transfer-Encoding', 'base64');

    fs.writeFile("somethingToCheckAgainst.pdf", resBase64, function(writeErr) {
     if(writeErr) {
            console.log(writeErr);
        } else {
            console.log("The file was saved!");
        }
    });
    console.log(resp.get('Content-Disposition'));
    resp.send(resBase64);
});

}

      

Client side function component:

xmlhttp.onreadystatechange = function()
{
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        var results = xmlhttp.responseText;

        window.open("data:application/pdf;base64, " + results);
    }
}

      

This is a quick and dirty prototype, so I am not interested in speed and dirty code, but clean and simple. How can I open a PDF that opens in a new window or a volume that is created on the server to display correctly (not completely empty)? Is the issue with node interacting with data from SSRS or is it caused by the way I process the data?

+3


source to share


2 answers


I have had the same problem for some time now. Trying to be a good citizen and posting what solved it for me after days of troubleshooting, as the answer doesn't seem to show up anywhere on the internet.



Add another option to httpntlm.post - "binary: true". If you don't, it turns out that response.body is automatically converted to a UTF-8 string, which corrupts the PDF and makes it appear empty.

+2


source


Even if it http-ntlm

doesn't give you the ability to stream data, you don't need to write the data to disk, and you don't need to base64 encode it. You can just create your own readable stream. The substack resumer

npm module makes this nice and clean. Running in its ntlm callback, it would look something like this:

var resumer = require('resumer');

function (err, res){ //-Returns once the stream is complete
    if(err) return next(err);

    var buffer = new Buffer(res.body);
    var stream = resumer().queue(buffer).end();

    resp.setHeader('Content-disposition', 'attachment; filename=out.pdf');
    resp.setHeader('Content-type', 'application/pdf');
    stream
        .pipe(resp)
        .on('error', next);
};

      



Please note that I am using the third middleware parameter next

to handle any errors. Just doing if(err) return err;

as you do will not help.

+1


source







All Articles