Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJs: binary fs.createReadStream streamed as UTF-8

My task at hand was to read a jpeg file in NodeJs and send it as http-response to answer a server request. Seemed to be trivial. However, my first solution failed. The browser did receive some binary gibrish, that was about 30% larger than the original file.

My code was (simplified; res is of type SeverResponse):

...
var fs = require('fs');
var stream = fs.createReadStream(pathToJPEG, {encoding: 'binary'});
res.setHeader('Content-Type', "image/jpeg");
stream.pipe(res);
...

As it turned out, what arrived at the browser was the UTF-8 encoded version of my source data. I also was able to exlude the response object to be the culprit. When I gave it an alternative stream (from Buffer, not file) it worked just fine.

Turns out the solution to my problem was to drop the option {encoding: 'binary'}. With that my browser received the right picture:

...
var fs = require('fs');
var stream = fs.createReadStream(pathToJPEG);
res.setHeader('Content-Type', "image/jpeg");
stream.pipe(res);
...

My question is: Why?

It seems intuitive that the first non-working version should be the correct one since it explicitly declares how to read the file.

like image 489
Daniel Avatar asked Nov 28 '15 20:11

Daniel


People also ask

What is FS createReadStream?

The function fs. createReadStream() allows you to open up a readable stream in a very simple manner. All you have to do is pass the path of the file to start streaming in. It turns out that the response (as well as the request) objects are streams.

Is createReadStream synchronous?

createReadStream() appears to have a synchronous interface. It does not return a promise or accept a callback to communicate back when it's done or to send back some results. So, it appears synchronous from the interface.


2 Answers

This is because the binary encoding is not really binary. createReadStream uses the same encoding parameters that accepted by Buffer. From the Node Buffer Docs:

'binary' - A way of encoding the buffer into a one-byte (i.e. latin-1) encoded string. The string 'latin-1' is not supported. Instead simply pass 'binary' to use 'latin-1' encoding.

Just set encoding to null to get the raw stream or buffer, or don't specify anything at all, as you did in your second example.

like image 192
lxe Avatar answered Sep 29 '22 23:09

lxe


Ixe is correct, changing encoding to null worked, but only after upgrading to a newer node/express package. Here is my code that correctly uploads a tar file:

    fs.exists(filePath, function(exists){
    if (exists) {
        var stat = fs.statSync(filePath);
        console.log('sending file, size %d', stat.size);

        res.writeHead(200, {
            "Content-Type": "application/x-tar",
            "Content-Disposition": "attachment; filename=" + filePath,
            "Content-Length": stat.size,
            "Content-Transfer-Encoding": "binary"
        });
        fs.createReadStream(filePath, { encoding: null }).pipe(res);    //must set encoding to null, as binary is not treated correctly

    } else {
        console.log('file not exist.');
        res.writeHead(400, {"Content-Type": "text/plain"});
        res.end("ERROR File does not exist");
    }
});
like image 37
Tom Avatar answered Sep 29 '22 23:09

Tom