Servidor de archivos estático básico en NodeJS

Estoy tratando de crear un servidor de archivos estáticos en nodejs más como un ejercicio para entender el nodo que como un servidor perfecto. Conozco bien proyectos como Connect y node-static y tengo la intención de usar esas bibliotecas para obtener más código listo para producción, pero también me gusta entender los conceptos básicos de lo que estoy trabajando. Con eso en mente, he codificado un pequeño servidor.js:

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(), uri); path.exists(filename, function(exists) { if(!exists) { console.log("not exists: " + filename); res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('404 Not Found\n'); res.end(); } var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; res.writeHead(200, mimeType); var fileStream = fs.createReadStream(filename); fileStream.pipe(res); }); //end path.exists }).listen(1337); 

Mi pregunta es doble

  1. ¿Es esta la manera “correcta” de crear y transmitir html básico, etc. en un nodo o hay un método mejor / más elegante / más robusto?

  2. ¿El .pipe () en el nodo básicamente hace lo siguiente?

.

 var fileStream = fs.createReadStream(filename); fileStream.on('data', function (data) { res.write(data); }); fileStream.on('end', function() { res.end(); }); 

¡Gracias a todos!

  • Su servidor básico se ve bien, excepto:

    Hay una statement de return faltante.

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

    Y:

    res.writeHead(200, mimeType);

    debiera ser:

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

  • Yes pipe() básicamente hace eso, también hace una pausa / reanuda la transmisión de la fuente (en caso de que el receptor sea más lento). Aquí está el código fuente de la función pipe() : https://github.com/joyent/node/blob/master/lib/stream.js

Menos es más

Simplemente ingrese el símbolo del sistema primero en su proyecto y use

 $ npm install express 

Luego escribe tu código app.js así:

 var express = require('express'), app = express(), port = process.env.PORT || 4000; app.use(express.static(__dirname + '/public')); app.listen(port); 

Luego, crearía una carpeta “pública” donde colocaría sus archivos. Lo intenté de la manera más difícil primero, pero tienes que preocuparte por los tipos de mimo, que es solo tener que mapear cosas que consumen mucho tiempo y luego preocuparte por los tipos de respuesta, etc. etc. etc … no, gracias.

Me gusta entender lo que sucede debajo del capó también.

Noté algunas cosas en tu código que probablemente quieras limpiar:

  • Se bloquea cuando el nombre del archivo apunta a un directorio, porque exists es verdadero e intenta leer una secuencia de archivos. Usé fs.lstatSync para determinar la existencia del directorio.

  • No está utilizando correctamente los códigos de respuesta HTTP (200, 404, etc.)

  • Mientras se determina MimeType (desde la extensión del archivo), no se está configurando correctamente en res.writeHead (como Stewe señaló)

  • Para manejar caracteres especiales, es probable que desee volver a abrir el uri

  • Sigue ciegamente los enlaces simbólicos (podría ser un problema de seguridad)

Dado esto, algunas de las opciones de apache (FollowSymLinks, ShowIndexes, etc.) comienzan a tener más sentido. Actualicé el código para su servidor de archivos simple de la siguiente manera:

 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); 

¿Qué hay de este patrón, que evita verificar por separado que el archivo existe

  var fileStream = fs.createReadStream(filename); fileStream.on('error', function (error) { response.writeHead(404, { "Content-Type": "text/plain"}); response.end("file not found"); }); fileStream.on('open', function() { var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; response.writeHead(200, {'Content-Type': mimeType}); }); fileStream.on('end', function() { console.log('sent file ' + filename); }); fileStream.pipe(response); 

Hice una función httpServer con funciones adicionales para uso general basadas en la respuesta de @Jeff Ward

  1. custtom dir
  2. index.html devuelve si req === dir

Uso:

 httpServer(dir).listen(port); 

https://github.com/kenokabe/ConciseStaticHttpServer

Gracias.

 var http = require('http') var fs = require('fs') var server = http.createServer(function (req, res) { res.writeHead(200, { 'content-type': 'text/plain' }) fs.createReadStream(process.argv[3]).pipe(res) }) server.listen(Number(process.argv[2])) 

El módulo st hace que sea más fácil servir archivos estáticos. Aquí hay un extracto de README.md:

 var mount = st({ path: __dirname + '/static', url: '/static' }) http.createServer(function(req, res) { var stHandled = mount(req, res); if (stHandled) return else res.end('this is not a static file') }).listen(1338) 

La respuesta de @JasonSebring me indicó la dirección correcta, sin embargo, su código está desactualizado. Así es como lo haces con la versión de connect más nueva.

 var connect = require('connect'), serveStatic = require('serve-static'), serveIndex = require('serve-index'); var app = connect() .use(serveStatic('public')) .use(serveIndex('public', {'icons': true, 'view': 'details'})) .listen(3000); 

En connect GitHub Repository hay otros middlewares que puede usar.