Próbuję stworzyć statyczny serwer plików w nodejs bardziej jako ćwiczenie do zrozumienia węzła niż jako doskonały serwer. Dobrze znam projekty takie jak Connect i node-static i zamierzam w pełni wykorzystać te biblioteki do tworzenia kodu gotowego do produkcji, ale lubię też rozumieć podstawy tego, nad czym pracuję. Mając to na uwadze, napisałem mały 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);
Moje pytanie jest dwojakie
Czy jest to „właściwy” sposób tworzenia i przesyłania strumieniowego podstawowego kodu HTML itp. W węźle, czy też istnieje lepsza / bardziej elegancka / solidniejsza metoda?
Czy .pipe () w węźle zasadniczo wykonuje następujące czynności?
.
var fileStream = fs.createReadStream(filename);
fileStream.on('data', function (data) {
res.write(data);
});
fileStream.on('end', function() {
res.end();
});
Dziękuję wszystkim!
fs.exists()
zamiastpath.exists()
w powyższym kodzie. Twoje zdrowie! i tak! nie zapomnijreturn
:fs.exists()
jest przestarzały . Stosowaniefs.access()
lub nawet lepiej jak w powyższym przypadku użycia,fs.stat()
. 2)url.parse
jest przestarzałe ;new URL
zamiast tego użyj nowszego interfejsu.Odpowiedzi:
Twój podstawowy serwer wygląda dobrze, z wyjątkiem:
Brakuje
return
oświadczenia.res.write('404 Not Found\n'); res.end(); return; // <- Don't forget to return here !!
I:
res.writeHead(200, mimeType);
Powinien być:
res.writeHead(200, {'Content-Type':mimeType});
Tak
pipe()
, w zasadzie to również wstrzymuje / wznawia strumień źródłowy (w przypadku, gdy odbiornik jest wolniejszy). Oto kod źródłowypipe()
funkcji: https://github.com/joyent/node/blob/master/lib/stream.jsźródło
Mniej znaczy więcej
Po prostu przejdź najpierw do wiersza poleceń w swoim projekcie i użyj
Następnie napisz swój kod app.js w następujący sposób:
var express = require('express'), app = express(), port = process.env.PORT || 4000; app.use(express.static(__dirname + '/public')); app.listen(port);
Utworzyłbyś wtedy folder „publiczny”, w którym umieszczasz swoje pliki. Najpierw spróbowałem tego trudniej, ale musisz się martwić typami mime, które wymagają tylko mapowania rzeczy, które są czasochłonne, a następnie martwienia się o typy odpowiedzi itp. Itd. Itd. Nie, dziękuję.
źródło
require('http')
w drugiej linii?Lubię też rozumieć, co się dzieje pod maską.
Zauważyłem kilka rzeczy w Twoim kodzie, które prawdopodobnie chcesz wyczyścić:
Awaria, gdy nazwa_pliku wskazuje na katalog, ponieważ istnieje prawda i próbuje odczytać strumień pliku. Użyłem fs.lstatSync do określenia istnienia katalogu.
Nie używa poprawnie kodów odpowiedzi HTTP (200, 404 itd.)
Podczas określania MimeType (z rozszerzenia pliku), nie jest on ustawiany poprawnie w res.writeHead (jak wskazał stewe)
Aby obsłużyć znaki specjalne, prawdopodobnie chcesz wyłączyć uri
Ślepo podąża za linkami symbolicznymi (może to stanowić zagrożenie dla bezpieczeństwa)
Biorąc to pod uwagę, niektóre opcje Apache (FollowSymLinks, ShowIndexes itp.) Zaczynają mieć więcej sensu. Zaktualizowałem kod twojego prostego serwera plików w następujący sposób:
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);
źródło
var mimeType = mimeTypes[path.extname(filename).match(/\.([^\.]+)$/)[1]];
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]))
źródło
A co z tym wzorcem, który pozwala uniknąć oddzielnego sprawdzania, czy plik istnieje
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);
źródło
fileStream.on('open', ...
Stworzyłem funkcję httpServer z dodatkowymi funkcjami do ogólnego użytku na podstawie odpowiedzi @Jeff Ward
Stosowanie:
https://github.com/kenokabe/ConciseStaticHttpServer
Dzięki.
źródło
moduł st sprawia obsługujących pliki statyczne łatwe. Oto fragment pliku 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)
źródło
Odpowiedź @JasonSebring wskazała mi właściwy kierunek, jednak jego kod jest nieaktualny. Oto, jak to zrobić w najnowszej
connect
wersji.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);
W
connect
repozytorium GitHub dostępne są inne oprogramowanie pośredniczące.źródło
connect
dokumentację, jest to tylkowrapper
formiddleware
. Wszystkie inne interesującemiddleware
pochodzą zexpress
repozytorium, więc technicznie rzecz biorąc, możesz użyć tych interfejsów API za pomocąexpress.use()
.