2016 Güncellemesi
Ekspres ve Ekspres olmadan gerçekten çalışan örnekler
Bu soru 5 yaşın üzerinde ama her yanıtın bazı sorunları var .
TL; DR
Aşağıdakilerle bir resim sunmak için örnekler için aşağı kaydırın:
express.static
express
connect
http
net
Tüm örnekler ayrıca GitHub'da: https://github.com/rsp/node-static-http-servers
Test sonuçları Travis'te mevcuttur: https://travis-ci.org/rsp/node-static-http-servers
Giriş
Bu sorunun sorulmasının üzerinden 5 yıldan fazla bir süre geçtikten sonra generalhenry tarafından yalnızca bir doğru cevap var ama bu cevap kodu ile herhangi bir sorun olsa bile, bazı sorunlar var gibi görünüyor alımı . "İşi yaptırmak için bir başkasına nasıl güvenileceğinden başka pek bir şey açıklamadığı" yorumunda bulunuldu ve kaç kişinin bu yorumu oylamış olması birçok şeyin açıklığa kavuşturulması gerektiğini açıkça gösteriyor.
Her şeyden önce, "Node.js kullanarak görüntüler nasıl sunulur" sorusunun iyi bir yanıtı, statik bir dosya sunucusunu sıfırdan uygulamak ve bunu kötü yapmaktır. İyi bir cevap işi doğru yapan Express gibi bir modül kullanmaktır .
Express kullanmanın "işi yapmak için başka birine nasıl güvenileceğinden başka pek bir şey açıklamadığını" söyleyen yorumları yanıtlarken , httpmodülü kullanmanın işi halletmek için zaten başka birine bağlı . Birisi işi yapmak için kimseye güvenmek istemiyorsa, bunun yerine en azından ham TCP soketleri kullanılmalıdır - bunu aşağıdaki örneklerimden birinde yapıyorum.
Daha ciddi bir sorun, burada httpmodülü kullanan tüm cevapların bozuk olmasıdır . Onlar tanıtmak yarış koşulları , güvensiz yol çözünürlüğünü yol açacaktır yol geçişi açığı , engelleme I / O tamamen edeceğini herhangi eşzamanlı istekleri hizmet için başarısız tüm diğer ince sorunlara - onlar tamamen soru sorar ne örnek olarak kırılır ve yine de httpTCP soketlerini kullanmak yerine modül tarafından sağlanan soyutlamayı zaten kullanıyorlar, böylece iddia ettikleri gibi her şeyi sıfırdan bile yapmıyorlar.
Soru "Statik dosya sunucusunu bir öğrenme alıştırması olarak sıfırdan nasıl uygulanır" ise, o zaman elbette bunun nasıl yapılacağına dair yanıtlar gönderilmelidir - ancak o zaman bile en azından bunların doğru olmasını beklemeliyiz . Ayrıca, bir görüntüyü sunmak isteyen birinin gelecekte daha fazla görüntü sunmak isteyebileceğini varsaymak mantıksız değildir, bu nedenle sabit kodlanmış yolla yalnızca tek bir dosya sunabilen belirli bir özel statik dosya sunucusu yazmanın biraz dar görüşlü. Bir görüntünün nasıl sunulacağına dair bir yanıt arayan birinin, herhangi bir görüntüye hizmet etmek için genel bir çözüm yerine yalnızca tek bir görüntüye hizmet eden bir çözümden memnun olacağını hayal etmek zor görünüyor.
Kısacası, soru bir resim ve bir de bunu uygun bir modül kullanmaktır bir cevap hizmet etmek nasıl güvenli preformant ve güvenilir bir şekilde olduğunu , okunabilir sürdürülebilir ve geleceğe kullanırken en iyi uygulama profesyonel düğüm geliştirme. Ancak, böyle bir yanıta büyük bir katkı, aynı işlevselliği manuel olarak uygulamanın bir yolunu göstereceğini kabul ediyorum, ancak ne yazık ki bunu yapmak için yapılan her girişim başarısız oldu. İşte bu yüzden bazı yeni örnekler yazdım.
Bu kısa girişten sonra, işte 5 farklı soyutlama seviyesinde işi yapan beş örneğim.
Minimum işlevsellik
Her örnek, publicdizindeki dosyalara hizmet eder ve aşağıdakilerin minimum işlevselliğini destekler:
- En yaygın dosyalar için MIME türleri
- HTML, JS, CSS, düz metin ve resimler sunar
index.htmlvarsayılan bir dizin indeksi olarak hizmet eder
- eksik dosyalar için hata kodlarıyla yanıt verir
- yol geçişi güvenlik açığı yok
- dosyaları okurken yarış koşulu yok
Her sürümü Node 4, 5, 6 ve 7 sürümlerinde test ettim.
express.static
Bu sürüm express.static, expressmodülün yerleşik ara yazılımını kullanır .
Bu örnek en fazla işlevselliğe ve en az miktarda koda sahiptir.
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
Bu sürüm expressmodülü kullanır ancak express.staticara katman yazılımı yoktur. Statik dosyaların sunulması, akışları kullanan tek bir yol işleyici olarak uygulanır.
Bu örnekte, basit yol geçiş önlemleri vardır ve en yaygın MIME türlerinin sınırlı bir kümesini destekler.
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
Bu sürüm, connect.m2'den bir düzey daha düşük olan modülü kullanır express.
Bu örnek, expresssürüme benzer işlevselliğe sahiptir, ancak biraz daha düşük kollu API'ler kullanır.
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
Bu sürüm, httpNode'da HTTP için en düşük seviyeli API olan modülü kullanır .
Bu örnek connectsürüme benzer işlevselliğe sahiptir, ancak daha düşük seviyeli API'ler kullanır.
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
Bu sürüm, netNode'daki TCP soketleri için en düşük seviyeli API olan modülü kullanır .
Bu örnek, httpsürümün bazı işlevlerine sahiptir, ancak minimal ve eksik HTTP protokolü sıfırdan uygulanmıştır. Parçalı kodlamayı desteklemediğinden, yanıt göndermeden önce boyutu bilmelerine hizmet etmeden önce dosyaları belleğe yükler çünkü dosyaların statü ve ardından yükleme bir yarış koşulu ortaya çıkarır.
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/');
});
Örnekleri indirin
Daha fazla açıklama ile tüm örnekleri GitHub'da yayınladım.
Örnekler ile express.static, express, connect, httpve net:
Yalnızca kullanan diğer proje express.static:
Testler
Test sonuçları Travis'te mevcuttur:
Her şey 4, 5, 6 ve 7 numaralı Düğüm sürümlerinde test edilir.
Ayrıca bakınız
Diğer ilgili cevaplar: