alexcres
alexcres

Reputation: 53

Node.js Web Server fs.createReadStream vs fs.readFile?

So I am writing my web server in pure node.js, only use bluebird for promisify. This has been bothering me for a week, and I can't decide which one should I really use. I have read tons of posts, blogs and docs on these two topic, please answer based on your own working experience, thanks. Here goes the detailed sum up and related questions.

Both approach has been tested, they both work great. But I can't test for performance, I only have my own basic website files(html, css, img, small database, etc.), and I never have managed video files and huge databases.

Below are code parts, to give you basic ideas(if you really know which one to use, don't bother reading the code, to save you some time), this question is not about logic, so you can just read the parts between dash lines.

About fs.createReadStream:
Pros: good for huge files, it reads a chunk at a time, saves memory and pipe is really smart.
Cons: Synchronous, can't be promisified(stream is a different concept to promise, too hard to do, not worth it).

//please ignore IP, its just a custom name for prototyping.
IP.read = function (fpath) {
    //----------------------------------------------------
    let file = fs.createReadStream(fpath);
    file.on('error', function () {
        return console.log('error on reading: ' + fpath);
    });
    return file;
    //----------------------------------------------------
};
//to set the response of onRequest(request, response) in http.createServer(onRequest).
IP.setResponse = function (fpath) {
    let ext = path.extname(fpath),
        data = IP.read(fpath);
    return function (resp) {
        //----------------------------------------------------
            //please ignore IP.setHeaders.
        resp.writeHead(200, IP.setHeaders(ext));
        data.pipe(resp).on('error', function (e) {
            cosnole.log('error on piping ' + fpath);
        });
        //----------------------------------------------------
    }
};

About fs.readFile:
Pros: Asynchronous, can easily be promisified, which makes code really easy to write(develop) and read(maintain). And other benefits I haven't gotten a hand on yet, like data validation, security, etc.
Cons: bad for huge files.

IP.read = function (fpath) {
    //----------------------------------------------------
    let file = fs.readFileAsync(fpath);
    return file;
    //----------------------------------------------------
};
//to set the response of onRequest(request, response) in http.createServer(onRequest).
IP.setResponse = function (fpath) {
    const ext = path.extname(fpath);
    return function (resp) {
        //----------------------------------------------------
        IP.read(fpath).then((data) => {
            resp.writeHead(200, IP.setHeaders(ext));
            resp.end(data);
        }).catch((e) => {
            console.log('Problem when reading: ' + fpath);
            console.log(e);
        });
        //----------------------------------------------------
    }
};

Here are my options:
• The easy way: Using fs.createReadStream for everything.
• The proper way: Using fs.createReadStream for huge files only.
• The practical way: Using fs.readFile for everything, until related problems occur, then handle those problems using fs.createReadStream.

My final decision is using fs.createReadStream for huge files only(I will create a function just for huge files), and fs.readFile for everything else. Is this a good/proper decision? Any better suggestions?

P.S.(not important):
I really like building infrastructure on my own, to give you an idea, when I instantiate a server, I can just set the routes like this, and customize whatever or however I want. Please don't suggest me to use frameworks:

let routes = [
    {
        method: ['GET', 'POST'],
        uri: ['/', '/home', '/index.html'],
        handleReq: function () {return app.setResp(homeP);}
    },
    {
        method: 'GET',
        uri: '/main.css',
        handleReq: function () {return app.setResp(maincssP);}
    },
    {
        method: 'GET',
        uri: '/test-icon.svg',
        handleReq: function () {return app.setResp(svgP);}
    },
    {
        method: 'GET',
        uri: '/favicon.ico',
        handleReq: function () {return app.setResp(iconP);}
    }
];

Or I can customize it and put it in a config.json file like this:

{
"routes":[
    {
        "method": ["GET", "POST"],
        "uri": ["/", "/home"],
        //I will create a function(handleReq) in my application to handle fpath
        "fpath": "./views/index.html"
    },
    {
        "method": "GET",
        "uri": "/main.css",
        "fpath": "./views/main.css"
    },
    {
        "method": "GET",
        "uri": "/test-icon.svg",
        "fpath": "./views/test-icon.svg"
    }
]
}

Upvotes: 2

Views: 3385

Answers (1)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276306

Let's discuss the actual practical way.

You should not be serving static files from Node.js in production

createReadStream and readFile are both very useful - createReadStream is more efficient in most cases and consider it if you're processing a lot of files (rather than serving them).

You should be serving static files from a static file server anyway - most PaaS web hosts do this for you automatically and if you set up an environment yourself you'll find yourself reverse-proxying node anyway behind something like IIS which should serve static files.

This is only true for static files, again, if you read them and transform them multiple times your question becomes very relevant.

For other purposes, you can use fs.readFileAsync safely

I use readFile a lot for reading files to buffers and working with them, while createReadStream can improve latency - overall you should get similar throughput and the API is easier to work with and more high level.

So in conclusion

  • If you're serving static files and care about performance - don't do it from Node.js in production anyway.
  • If you're transforming files as streams and latency is important, use createReadStream.
  • Otherwise prefer readFile.

Upvotes: 2

Related Questions