Estoy tratando de crear un servidor de archivos estático en nodejs más como un ejercicio para entender el nodo que como un servidor perfecto. Soy consciente de 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 comprender los conceptos básicos de lo que estoy trabajando. Con eso en mente, he codificado un pequeño server.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
¿Es esta la forma "correcta" de crear y transmitir html básico, etc. en el nodo o hay un método mejor / más elegante / más robusto?
¿El .pipe () en el nodo básicamente está haciendo lo siguiente?
.
var fileStream = fs.createReadStream(filename);
fileStream.on('data', function (data) {
res.write(data);
});
fileStream.on('end', function() {
res.end();
});
¡Gracias a todos!
fs.exists()
lugar depath.exists()
en el código anterior. ¡Salud! ¡y si! no olvidesreturn
:fs.exists()
está en desuso . Usofs.access()
o incluso mejor que para el caso de uso anterior,fs.stat()
. 2)url.parse
está en desuso ; utilice lanew URL
interfaz más nueva en su lugar.Respuestas:
Su servidor básico se ve bien, excepto:
Falta una
return
declaración.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});
Sí
pipe()
, básicamente hace eso, también pausa / reanuda el flujo de origen (en caso de que el receptor sea más lento). Aquí está el código fuente de lapipe()
función: https://github.com/joyent/node/blob/master/lib/stream.jsfuente
Menos es más
Simplemente vaya al símbolo del sistema primero en su proyecto y use
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. Primero lo intenté de la manera más difícil, pero tienes que preocuparte por los tipos de mímica, que es simplemente tener que mapear cosas, lo que lleva mucho tiempo y luego preocuparte por los tipos de respuesta, etc., etc., etc., no, gracias.
fuente
require('http')
en la segunda línea?También me gusta entender lo que sucede bajo el capó.
Noté algunas cosas en tu código que probablemente quieras limpiar:
Se bloquea cuando el nombre de archivo apunta a un directorio, porque existe es verdadero e intenta leer un flujo de archivo. Usé fs.lstatSync para determinar la existencia del directorio.
No está usando los códigos de respuesta HTTP correctamente (200, 404, etc.)
Mientras se determina MimeType (a partir de la extensión del archivo), no se configura correctamente en res.writeHead (como señaló Stewe)
Para manejar caracteres especiales, probablemente desee anular el escape del uri
Sigue ciegamente 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);
fuente
var mimeType = mimeTypes[path.extname(filename).match(/\.([^\.]+)$/)[1]];
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]))
fuente
¿Qué tal 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);
fuente
fileStream.on('open', ...
Hice una función httpServer con características adicionales para uso general según la respuesta de @Jeff Ward
Uso:
https://github.com/kenokabe/ConciseStaticHttpServer
Gracias.
fuente
el módulo st facilita el servicio de 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)
fuente
La respuesta de @JasonSebring me indicó la dirección correcta, sin embargo, su código está desactualizado. Así es como se hace con la
connect
versión más reciente.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 el
connect
repositorio de GitHub hay otros middlewares que puede usar.fuente
connect
documentación, es solo unwrapper
formiddleware
. Todos los demás interesantesmiddleware
provienen delexpress
repositorio, por lo que técnicamente podría usar esas API usando elexpress.use()
.