2016年アップデート
ExpressありとExpressなしの実際に機能する例
この質問は5年以上前のものですが、すべての回答にいくつかの問題があります。
TL; DR
下にスクロールして、画像を提供する例を示します。
express.static
express
connect
http
net
すべての例はGitHubにもあります:https : //github.com/rsp/node-static-http-servers
テスト結果はTravisで入手できます:https : //travis-ci.org/rsp/node-static-http-servers
前書き
この質問が出されてから5年以上が経ちましたが、generalhenryの正解は1つだけですが、その答えはコードに問題はありませんが、受信に問題があるようです。それは「仕事を成し遂げるために他の誰かに頼る方法以外はあまり説明しない」とコメントされ、多くの人々がこのコメントに投票したという事実は多くのことが明確化を必要とすることを明らかに示しています。
まず、「Node.jsを使用して画像を提供する方法」に対する適切な答えは、静的ファイルサーバーを最初から実装するのではなく、ひどいことです。良い答えは、正しく機能する Expressのようなモジュールを使用することです。
Expressを使用すると「他の人に頼って仕事を終わらせる方法以外はあまり説明されない」というコメントに答えると、httpモジュールの使用はすでに他の人に頼って仕事を終えていることに注意してください。誰かが誰かに頼って仕事をやりたくない場合は、代わりに少なくとも生のTCPソケットを使用する必要があります。これは、以下の私の例の1つで行います。
より深刻な問題は、httpモジュールを使用するここでのすべての回答が壊れていることです。彼らは、紹介、レース条件、安全でないパスの解決につながるパストラバーサルの脆弱性、I / Oをブロックし、完全になります任意の同時要求提供するために失敗し、彼らが完全に質問がについて尋ねるものの例として壊れている、と-すべておよびその他の微妙な問題ではしかし、httpTCPソケットを使用する代わりに、モジュールが提供する抽象化をすでに使用しているため、要求どおりに最初からすべてを実行することすらありません。
質問が「静的ファイルサーバーを最初から実装する方法、学習課題として」である場合、それを実行する方法は必ず回答されますが、それでも、少なくとも正しいことが期待されます。また、画像を提供したい人が将来さらに多くの画像を提供したいと思うので、ハードコードされたパスを持つ単一のファイルのみを提供できる特定のカスタム静的ファイルサーバーを書くことは、やや近視。画像を提供する方法についての回答を検索する人が、画像を提供する一般的なソリューションではなく、単一の画像のみを提供するソリューションで満足することは想像に難く思えます。
要するに、問題は、画像をどのように提供するかであり、その答えは、適切なモジュールを使用して、プロフェッショナルノードのベストプラクティスを使用しながら、読み取り可能で、保守可能で、将来も保証された、安全で予備的かつ信頼できる方法でそれを実行することです。開発。しかし、私はそのような答えに大きな追加が同じ機能を手動で実装する方法を示すことになることに同意しますが、残念ながらそれを行うすべての試みはこれまで失敗しました。そしてそれが私がいくつかの新しい例を書いた理由です。
この短い紹介の後、5つの異なる抽象化レベルで作業を行う5つの例を示します。
最小限の機能
すべての例は、publicディレクトリからファイルを提供し、以下の最小機能をサポートしています。
- 最も一般的なファイルのMIMEタイプ
- HTML、JS、CSS、プレーンテキストおよび画像を提供します
index.htmlデフォルトのディレクトリインデックスとして機能します
- 不足しているファイルのエラーコードで応答します
- パストラバーサルの脆弱性なし
- ファイルの読み取り中に競合状態が発生しない
Nodeバージョン4、5、6、7ですべてのバージョンをテストしました。
express.static
このバージョンはexpress.static、expressモジュールの組み込みミドルウェアを使用します。
この例では、ほとんどの機能と最小限のコードを使用しています。
var path = require('path');
var express = require('express');
var app = express();
var dir = path.join(__dirname, 'public');
app.use(express.static(dir));
app.listen(3000, function () {
console.log('Listening on http://localhost:3000/');
});
express
このバージョンはexpressモジュールを使用しますが、express.staticミドルウェアはありません。静的ファイルの提供は、ストリームを使用する単一のルートハンドラーとして実装されます。
この例には、単純なパストラバーサル対策があり、最も一般的なMIMEタイプの限定されたセットをサポートしています。
var path = require('path');
var express = require('express');
var app = express();
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
app.get('*', function (req, res) {
var file = path.join(dir, req.path.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
return res.status(403).end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', function () {
res.set('Content-Type', type);
s.pipe(res);
});
s.on('error', function () {
res.set('Content-Type', 'text/plain');
res.status(404).end('Not found');
});
});
app.listen(3000, function () {
console.log('Listening on http://localhost:3000/');
});
connect
このバージョンconnectは、1つ下のレベルの抽象化であるモジュールを使用しますexpress。
この例は、expressバージョンと同様の機能を備えていますが、レバーが少し低いAPIを使用しています。
var path = require('path');
var connect = require('connect');
var app = connect();
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
app.use(function (req, res) {
var reqpath = req.url.toString().split('?')[0];
if (req.method !== 'GET') {
res.statusCode = 501;
res.setHeader('Content-Type', 'text/plain');
return res.end('Method not implemented');
}
var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
res.statusCode = 403;
res.setHeader('Content-Type', 'text/plain');
return res.end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', function () {
res.setHeader('Content-Type', type);
s.pipe(res);
});
s.on('error', function () {
res.setHeader('Content-Type', 'text/plain');
res.statusCode = 404;
res.end('Not found');
});
});
app.listen(3000, function () {
console.log('Listening on http://localhost:3000/');
});
http
このバージョンでは、httpNodeのHTTPの最低レベルのAPIであるモジュールを使用します。
この例には、connectバージョンと同様の機能がありますが、さらに低いレベルのAPIを使用しています。
var path = require('path');
var http = require('http');
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
var server = http.createServer(function (req, res) {
var reqpath = req.url.toString().split('?')[0];
if (req.method !== 'GET') {
res.statusCode = 501;
res.setHeader('Content-Type', 'text/plain');
return res.end('Method not implemented');
}
var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
res.statusCode = 403;
res.setHeader('Content-Type', 'text/plain');
return res.end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', function () {
res.setHeader('Content-Type', type);
s.pipe(res);
});
s.on('error', function () {
res.setHeader('Content-Type', 'text/plain');
res.statusCode = 404;
res.end('Not found');
});
});
server.listen(3000, function () {
console.log('Listening on http://localhost:3000/');
});
net
このバージョンは、netNodeのTCPソケットの最低レベルのAPIであるモジュールを使用します。
この例には、httpバージョンの機能の一部が含まれていますが、最小限の不完全なHTTPプロトコルが最初から実装されています。チャンクエンコーディングをサポートしていないため、ファイルを統計してからロードすると競合状態が発生するため、応答を送信する前にサイズを確認する前にファイルをメモリにロードします。
var path = require('path');
var net = require('net');
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
var server = net.createServer(function (con) {
var input = '';
con.on('data', function (data) {
input += data;
if (input.match(/\n\r?\n\r?/)) {
var line = input.split(/\n/)[0].split(' ');
var method = line[0], url = line[1], pro = line[2];
var reqpath = url.toString().split('?')[0];
if (method !== 'GET') {
var body = 'Method not implemented';
con.write('HTTP/1.1 501 Not Implemented\n');
con.write('Content-Type: text/plain\n');
con.write('Content-Length: '+body.length+'\n\n');
con.write(body);
con.destroy();
return;
}
var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
var body = 'Forbidden';
con.write('HTTP/1.1 403 Forbidden\n');
con.write('Content-Type: text/plain\n');
con.write('Content-Length: '+body.length+'\n\n');
con.write(body);
con.destroy();
return;
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.readFile(file, function (err, data) {
if (err) {
var body = 'Not Found';
con.write('HTTP/1.1 404 Not Found\n');
con.write('Content-Type: text/plain\n');
con.write('Content-Length: '+body.length+'\n\n');
con.write(body);
con.destroy();
} else {
con.write('HTTP/1.1 200 OK\n');
con.write('Content-Type: '+type+'\n');
con.write('Content-Length: '+data.byteLength+'\n\n');
con.write(data);
con.destroy();
}
});
}
});
});
server.listen(3000, function () {
console.log('Listening on http://localhost:3000/');
});
例をダウンロード
すべての例をGitHubに詳細な説明とともに投稿しました。
例とexpress.static、express、connect、httpとnet:
のみを使用する他のプロジェクトexpress.static:
テスト
テスト結果はTravisで入手できます。
すべてがNodeバージョン4、5、6、7でテストされています。
こちらもご覧ください
その他の関連する回答: