Основной статический файловый сервер в NodeJS

Я пытаюсь создать статический файловый сервер в nodejs больше как упражнение, чтобы понять node, чем как идеальный сервер. Мне хорошо знакомы с такими проектами, как Connect и node -static, и полностью намерены использовать эти библиотеки для получения более готового к выпуску кода, но мне также нравится понимать основы того, с чем я работаю. Имея это в виду, я закодировал небольшой 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);

Мой вопрос двоякий

  • Является ли это "правильным" способом создания и потоковой передачи базового html и т.д. в node или есть лучший/более элегантный/более надежный метод?

  • Является ли .pipe() в node в основном просто следующим:

.

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

Спасибо всем!

Ответ 1

  • Ваш основной сервер выглядит хорошо, кроме:

    Отсутствует инструкция return.

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

    и

    res.writeHead(200, mimeType);

    должен быть:

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

  • Да pipe() делает в основном это, он также приостанавливает/возобновляет поток источника (в случае, если приемник работает медленнее). Вот исходный код функции pipe(): https://github.com/joyent/node/blob/master/lib/stream.js

Ответ 2

Меньше больше

Просто запустите команду сначала в своем проекте и используйте

$ npm install express

Затем напишите свой код app.js следующим образом:

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

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

Затем вы создадите "общедоступную" папку, в которую вы поместите свои файлы. Сначала я попробовал это, но вам нужно беспокоиться о типах mime, которые просто должны отображать материал, который требует много времени, а затем беспокоиться о типах ответов и т.д. И т.д. И т.д.... нет, спасибо.

Ответ 3

Мне нравится понимать, что происходит под капотом.

Я заметил несколько вещей в вашем коде, которые вы, вероятно, захотите очистить:

  • Сбой, когда имя файла указывает на каталог, потому что существует true, и он пытается прочитать поток файлов. Я использовал fs.lstatSync для определения существования каталога.

  • Он неправильно использует коды ответа HTTP (200, 404 и т.д.)

  • В то время как MimeType определяется (из расширения файла), он не устанавливается корректно в res.writeHead(как указано stewe)

  • Чтобы обрабатывать специальные символы, вы, вероятно, хотите unescape uri

  • Он слепо следует за символическими ссылками (может быть проблемой безопасности)

Учитывая это, некоторые из параметров apache (FollowSymLinks, ShowIndexes и т.д.) начинают иметь больше смысла. Я обновляю код для вашего простого файлового сервера следующим образом:

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

Ответ 4

Как насчет этого шаблона, который позволяет избежать проверки отдельно, что файл существует

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

Ответ 5

Я сделал функцию httpServer с дополнительными функциями для общего использования на основе ответа @Jeff Ward

  • custtom dir
  • index.html возвращает if req === dir

Применение:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

Спасибо.

Ответ 6

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

Ответ 7

st-модуль упрощает обслуживание статических файлов. Вот выдержка из 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)

Ответ 8

@JasonSebring ответ указал мне в правильном направлении, однако его код устарел. Вот как вы это делаете с новейшей версией 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);

В connect Репозиторий GitHub есть другие посредники, которые вы можете использовать.