Basic static file server in NodeJS

Less is more

Just go command prompt first on your project and use

$ npm install express

Then write your app.js code like so:

var express = require('express'),
app = express(),
port = process.env.PORT || 4000;

app.use(express.static(__dirname + '/public'));
app.listen(port);

You would then create a "public" folder where you place your files. I tried it the harder way first but you have to worry about mime types which is just having to map stuff which is time consuming and then worry about response types, etc. etc. etc.... no thank you.


  • Your basic server looks good, except:

    There is a return statement missing.

    res.write('404 Not Found\n');
    res.end();
    return; // <- Don't forget to return here !!
    

    And:

    res.writeHead(200, mimeType);

    should be:

    res.writeHead(200, {'Content-Type':mimeType});

  • Yes pipe() does basically that, it also pauses/resumes the source stream (in case the receiver is slower). Here is the source code of the pipe() function: https://github.com/joyent/node/blob/master/lib/stream.js


I like understanding what's going on under the hood as well.

I noticed a few things in your code that you probably want to clean up:

  • It crashes when filename points to a directory, because exists is true and it tries to read a file stream. I used fs.lstatSync to determine directory existence.

  • It isn't using the HTTP response codes correctly (200, 404, etc)

  • While MimeType is being determined (from the file extension), it isn't being set correctly in res.writeHead (as stewe pointed out)

  • To handle special characters, you probably want to unescape the uri

  • It blindly follows symlinks (could be a security concern)

Given this, some of the apache options (FollowSymLinks, ShowIndexes, etc) start to make more sense. I've update the code for your simple file server as follows:

var http = require('http'),
    url = require('url'),
    path = require('path'),
    fs = require('fs');
var mimeTypes = {
    "html": "text/html",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "js": "text/javascript",
    "css": "text/css"};

http.createServer(function(req, res) {
  var uri = url.parse(req.url).pathname;
  var filename = path.join(process.cwd(), unescape(uri));
  var stats;

  try {
    stats = fs.lstatSync(filename); // throws if path doesn't exist
  } catch (e) {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.write('404 Not Found\n');
    res.end();
    return;
  }


  if (stats.isFile()) {
    // path exists, is a file
    var mimeType = mimeTypes[path.extname(filename).split(".").reverse()[0]];
    res.writeHead(200, {'Content-Type': mimeType} );

    var fileStream = fs.createReadStream(filename);
    fileStream.pipe(res);
  } else if (stats.isDirectory()) {
    // path exists, is a directory
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write('Index of '+uri+'\n');
    res.write('TODO, show index?\n');
    res.end();
  } else {
    // Symbolic link, other?
    // TODO: follow symlinks?  security?
    res.writeHead(500, {'Content-Type': 'text/plain'});
    res.write('500 Internal server error\n');
    res.end();
  }

}).listen(1337);