it-swarm.com.de

Statischer Dateiserver in NodeJS

Ich versuche, einen statischen Dateiserver in nodejs eher als Übung zum Verstehen des Knotens als als perfekten Server zu erstellen. Ich kenne Projekte wie Connect und node-static gut und beabsichtige, diese Bibliotheken für produktionsfertigen Code zu verwenden, aber ich verstehe auch gerne die Grundlagen dessen, woran ich arbeite. In diesem Sinne habe ich einen kleinen server.js programmiert:

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

Meine Frage ist zweifach 

  1. Ist dies der "richtige" Weg, um einfache HTML-Dateien usw. im Knoten zu erstellen und zu streamen, oder gibt es eine bessere/elegantere/robustere Methode?

  2. Tut das .pipe () im Knoten im Grunde nur Folgendes?

.

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

Vielen Dank an alle!

83
slapthelownote
  • Ihr Basisserver sieht gut aus, außer:

    Es fehlt eine return -Anweisung.

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

    Und:

    res.writeHead(200, mimeType);

    sollte sein:

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

  • Ja, pipe() erledigt dies im Grunde genommen und pausiert/setzt den Quell-Stream fort (falls der Empfänger langsamer ist). Hier ist der Quellcode der Funktion pipe(): https://github.com/joyent/node/blob/master/lib/stream.js

44
stewe

Weniger ist mehr

Gehen Sie einfach in Ihrem Projekt auf Befehl Prompt und verwenden Sie es 

$ npm install express

Dann schreibe deinen app.js-Code wie folgt:

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

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

Sie erstellen dann einen "öffentlichen" Ordner, in dem Sie Ihre Dateien ablegen. Ich habe es zuerst auf die härtere Art ausprobiert, aber Sie müssen sich um MIME-Typen sorgen, was nur zeitraubendes Material und dann die Beantwortung von Antworttypen usw. usw. erforderlich macht.

55
Jason Sebring

Ich verstehe auch gerne, was unter der Haube vorgeht.

Ich habe ein paar Dinge in Ihrem Code bemerkt, die Sie wahrscheinlich bereinigen möchten:

  • Es stürzt ab, wenn Dateiname auf ein Verzeichnis verweist, da Existenz wahr ist und versucht, einen Dateistream zu lesen. Ich habe fs.lstatSync verwendet, um das Vorhandensein von Verzeichnissen festzustellen.

  • Die HTTP-Antwortcodes werden nicht ordnungsgemäß verwendet (200, 404 usw.).

  • Während MimeType ermittelt wird (von der Dateierweiterung), wird es in res.writeHead nicht richtig festgelegt (wie Stewewe darauf hinweist).

  • Um mit Sonderzeichen umgehen zu können, möchten Sie wahrscheinlich den URI aufheben

  • Es folgt blind Symlinks (kann ein Sicherheitsbedenken sein)

Aus diesem Grund machen einige Apache-Optionen (FollowSymLinks, ShowIndexes usw.) mehr Sinn. Ich habe den Code für Ihren einfachen Dateiserver wie folgt aktualisiert:

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

Wie wäre es mit diesem Muster, um zu vermeiden, dass die Datei separat geprüft wird

        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

Ich habe eine httpServer-Funktion mit zusätzlichen Funktionen für die allgemeine Verwendung erstellt, die auf der Antwort von @Jeff Ward basiert

  1. custtom dir 
  2. index.html gibt zurück, wenn req === dir

Verwendungszweck:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

Vielen Dank.

2
Ken OKABE

Antwort von @JasonSebring wies mich in die richtige Richtung, jedoch ist sein Code veraltet. So machen Sie es mit der neuesten connect-Version.

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

In connectGitHub Repository können Sie andere Middlewares verwenden.

0
ffleandro

das st Modul macht das Versorgen statischer Dateien einfach. Hier ist ein Auszug aus 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