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"
}
]
}
fs.createReadStream()? It works, it's easy to use, it's much better for memory consumption, and the streaming itself isn't synchronous so it won't block your app.createReadStream()as it returns immediately anyway. Don't overuse promises "just because".