web-dev-qa-db-ja.com

NodeJSの基本的な静的ファイルサーバー

私は完璧なサーバーとしてよりもノードを理解するための演習として、nodejsで静的ファイルサーバーを作成しようとしています。私は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);

私の質問は二つあります

  1. これはノードで基本的なhtmlなどを作成してストリーミングするための「正しい」方法ですか、それともより良い/よりエレガントな/より堅牢な方法がありますか?

  2. ノードの.pipe()は基本的に次のことをしていますか?

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

みんな、ありがとう!

84
slapthelownote
  • 以下を除いて、基本的なサーバーは良好に見えます。

    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

44
stewe

少ないほうがいいですね

プロジェクトで最初にコマンドプロンプトを実行して使用するだけです

$ 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タイプを心配する必要があります。これは時間のかかるものをマッピングするだけであり、応答タイプなどを心配するなど...ありがとうございません。

56
Jason Sebring

ボンネットの下で何が起こっているのかを理解するのも好きです。

おそらくクリーンアップしたいコードのいくつかのことに気付きました。

  • Existsがディレクトリを指すとクラッシュします。existesがtrueで、ファイルストリームを読み取ろうとするためです。 fs.lstatSyncを使用して、ディレクトリの存在を確認しました。

  • HTTP応答コードを正しく使用していません(200、404など)

  • MimeTypeは(ファイル拡張子から)決定されていますが、res.writeHeadで正しく設定されていません(steweが指摘したように)

  • 特殊文字を処理するには、おそらく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);
19
Jeff Ward

ファイルが存在することを個別にチェックすることを回避するこのパターンはどうですか

        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

@Jeff Wardの回答に基づいて、一般的な使用のための追加機能を備えたhttpServer関数を作成しました

  1. カストムディレクトリ
  2. index.htmlは、req === dirの場合に戻ります

使用法:

httpServer(dir).listen(port);

https://github.com/kenokabe/ConciseStaticHttpServer

ありがとう。

2
user1028880

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

connectGitHub Repository には、使用できる他のミドルウェアがあります。

0
ffleandro

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)
0
kaore