web-dev-qa-db-fra.com

Serveur de fichiers statique de base dans NodeJS

J'essaie de créer un serveur de fichiers statique dans nodejs plus comme un exercice pour comprendre le noeud que comme un serveur parfait. Je connais bien des projets tels que Connect et node-static et j'ai bien l'intention d'utiliser ces bibliothèques pour un code plus prêt à la production, mais j'aime aussi comprendre les bases de ce avec quoi je travaille. Dans cet esprit, j'ai codé un petit 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);

Ma question est double 

  1. Est-ce la bonne façon de créer et de diffuser du code HTML de base, etc. dans un noeud, ou existe-t-il une méthode meilleure/plus élégante/plus robuste?

  2. Est-ce que le .pipe () dans le noeud fait juste ce qui suit?

.

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

Merci tout le monde!

83
slapthelownote
  • Votre serveur de base a l'air bien, sauf:

    Il manque une instruction return.

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

    Et:

    res.writeHead(200, mimeType);

    devrait être:

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

  • Oui pipe() fait essentiellement cela, il met également en pause/reprend le flux source (au cas où le récepteur est plus lent). Voici le code source de la fonction pipe(): https://github.com/joyent/node/blob/master/lib/stream.js

44
stewe

Moins est plus

Il suffit d’inviter l’invite de commande en premier sur votre projet et de l’utiliser. 

$ npm install express

Ensuite, écrivez votre code app.js comme suit:

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

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

Vous créez ensuite un dossier "public" dans lequel vous placez vos fichiers. J'ai d'abord essayé la méthode la plus difficile, mais vous devez vous préoccuper des types de mime, qui consistent simplement à mapper des éléments qui prennent beaucoup de temps, puis des types de réponse, etc., etc. etc.… non merci.

55
Jason Sebring

J'aime aussi comprendre ce qui se passe sous le capot.

J'ai remarqué quelques éléments dans votre code que vous souhaitez probablement nettoyer:

  • Il se bloque lorsque le nom de fichier pointe vers un répertoire, car existe est true et tente de lire un flux de fichiers. J'ai utilisé fs.lstatSync pour déterminer l'existence d'un répertoire.

  • Il n'utilise pas correctement les codes de réponse HTTP (200, 404, etc.)

  • Alors que MimeType est en cours de détermination (à partir de l'extension de fichier), il n'est pas défini correctement dans res.writeHead (comme l'a souligné stewe)

  • Pour manipuler des caractères spéciaux, vous voudrez probablement détourner l'uri

  • Il suit aveuglément les liens symboliques (pourrait être un problème de sécurité)

Compte tenu de cela, certaines des options Apache (FollowSymLinks, ShowIndexes, etc.) commencent à prendre plus de sens. J'ai mis à jour le code de votre serveur de fichiers simple comme suit:

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);
20
Jeff Ward

Que diriez-vous de ce modèle, qui évite de vérifier séparément que le fichier 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);
3
Aerik
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]))
2
Chí Nguyễn

J'ai créé une fonction httpServer avec des fonctionnalités supplémentaires pour une utilisation générale basée sur la réponse @Jeff Ward

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

Utilisation:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

Merci.

2
Ken OKABE

La réponse de @JasonSebring m'a orienté dans la bonne direction, mais son code est obsolète. Voici comment procéder avec la dernière version connect.

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

Dans connectGitHub Repository il existe d’autres middlewares que vous pouvez utiliser.

0
ffleandro

le module st facilite la desserte de fichiers statiques. Voici un extrait 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)
0
kaore