Mise à jour 2016
Exemples avec Express et sans Express qui fonctionnent réellement
Cette question date de plus de 5 ans, mais chaque réponse a quelques problèmes .
TL; DR
Faites défiler vers le bas pour voir des exemples pour diffuser une image avec:
express.static
express
connect
http
net
Tous les exemples sont également sur GitHub: https://github.com/rsp/node-static-http-servers
Les résultats des tests sont disponibles sur Travis: https://travis-ci.org/rsp/node-static-http-servers
introduction
Après plus de 5 ans depuis que cette question a été posée, il n'y a qu'une seule bonne réponse de generalhenry mais même si cette réponse ne pose aucun problème avec le code, elle semble avoir quelques problèmes de réception . Il a été commenté que cela "n'explique pas grand-chose d'autre que comment compter sur quelqu'un d'autre pour faire le travail" et le fait que de nombreuses personnes ont voté ce commentaire montre clairement que beaucoup de choses doivent être clarifiées.
Tout d'abord, une bonne réponse à "Comment servir des images à l'aide de Node.js" est de ne pas implémenter un serveur de fichiers statiques à partir de zéro et de le faire mal. Une bonne réponse consiste à utiliser un module comme Express qui fait correctement le travail .
En réponse aux commentaires qui disent que l'utilisation d'Express "n'explique pas grand chose d'autre que comment compter sur quelqu'un d'autre pour faire le travail", il faut noter que l'utilisation du httpmodule dépend déjà de quelqu'un d'autre pour faire le travail. Si quelqu'un ne veut compter sur personne pour faire le travail, alors au moins des sockets TCP bruts devraient être utilisés à la place - ce que je fais dans l'un de mes exemples ci-dessous.
Un problème plus grave est que toutes les réponses ici qui utilisent le httpmodule sont défectueuses . Ils introduisent des conditions de course , la résolution de chemin insécurité qui conduira à la vulnérabilité de parcours de chemin , le blocage d' E / S qui va complètement échouer à traiter des demandes simultanées à tous et d' autres problèmes subtils - ils sont complètement brisés comme des exemples de ce que la question porte sur, et pourtant ils utilisent déjà l'abstraction fournie par le httpmodule au lieu d'utiliser les sockets TCP, donc ils ne font même pas tout à partir de zéro comme ils le prétendent.
Si la question était «Comment implémenter le serveur de fichiers statiques à partir de zéro, comme exercice d'apprentissage», alors les réponses sur la façon de le faire devraient être affichées - mais même dans ce cas, nous devrions nous attendre à ce qu'elles soient au moins correctes . En outre, il n'est pas déraisonnable de supposer que quelqu'un qui souhaite servir une image pourrait vouloir servir plus d'images à l'avenir, de sorte que l'on pourrait affirmer que l'écriture d'un serveur de fichiers statiques personnalisé spécifique qui ne peut servir qu'un seul fichier avec un chemin codé en dur est un peu myope. Il semble difficile d'imaginer que quiconque cherche une réponse sur la façon de servir une image se contenterait d'une solution qui ne sert qu'une seule image au lieu d'une solution générale pour servir n'importe quelle image.
En bref, la question est de savoir comment servir une image et une réponse à cette question est d'utiliser un module approprié de le faire dans une manière sécurisée, preformant et fiable qui est lisible, maintenable et à l' épreuve tout en utilisant la meilleure pratique du nœud professionnel développement. Mais je suis d'accord qu'un excellent ajout à une telle réponse serait de montrer un moyen d'implémenter la même fonctionnalité manuellement, mais malheureusement, chaque tentative de le faire a échoué jusqu'à présent. Et c'est pourquoi j'ai écrit de nouveaux exemples.
Après cette brève introduction, voici mes cinq exemples faisant le travail sur 5 niveaux différents d'abstraction.
Fonctionnalité minimale
Chaque exemple sert des fichiers du publicrépertoire et prend en charge la fonctionnalité minimale de:
- Types MIME pour les fichiers les plus courants
- sert HTML, JS, CSS, texte brut et images
- sert
index.htmld'index de répertoire par défaut
- répond avec des codes d'erreur pour les fichiers manquants
- aucune vulnérabilité de traversée de chemin
- pas de conditions de course lors de la lecture des fichiers
J'ai testé toutes les versions sur les versions 4, 5, 6 et 7 de Node.
express.static
Cette version utilise le express.staticmiddleware expressintégré du module.
Cet exemple a le plus de fonctionnalités et le moins de code.
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
Cette version utilise le expressmodule mais sans le express.staticmiddleware. La diffusion de fichiers statiques est implémentée en tant que gestionnaire d'itinéraire unique à l'aide de flux.
Cet exemple présente des contre-mesures simples de traversée de chemin et prend en charge un ensemble limité de types MIME les plus courants.
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
Cette version utilise le connectmodule qui est un niveau d'abstraction inférieur à express.
Cet exemple a des fonctionnalités similaires à la expressversion mais en utilisant des API légèrement inférieures.
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
Cette version utilise le httpmodule qui est l'API de plus bas niveau pour HTTP dans Node.
Cet exemple a des fonctionnalités similaires à la connectversion, mais utilise encore plus d'API de niveau inférieur.
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
Cette version utilise le netmodule qui est l'API de plus bas niveau pour les sockets TCP dans Node.
Cet exemple présente certaines des fonctionnalités de la httpversion, mais le protocole HTTP minimal et incomplet a été implémenté à partir de zéro. Puisqu'il ne prend pas en charge le codage fragmenté, il charge les fichiers en mémoire avant de les servir pour connaître la taille avant d'envoyer une réponse, car le stattage des fichiers puis le chargement introduiraient une condition de concurrence.
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/');
});
Téléchargez des exemples
J'ai posté tous les exemples sur GitHub avec plus d'explications.
Exemples avec express.static, express, connect, httpet net:
Autre projet utilisant uniquement express.static:
Des tests
Les résultats des tests sont disponibles sur Travis:
Tout est testé sur les versions 4, 5, 6 et 7 de Node.
Voir également
Autres réponses connexes: