Atualização de 2016
Exemplos com Express e sem Express que realmente funcionam
Esta pergunta tem mais de 5 anos, mas todas as respostas têm alguns problemas .
TL; DR
Role para baixo para obter exemplos de imagem com:
express.static
express
connect
http
net
Todos os exemplos também estão no GitHub: https://github.com/rsp/node-static-http-servers
Os resultados dos testes estão disponíveis no Travis: https://travis-ci.org/rsp/node-static-http-servers
Introdução
Depois de mais de 5 anos desde que essa pergunta foi feita, existe apenas uma resposta correta pelo generalhenry, mas mesmo que essa resposta não tenha problemas com o código, ela parece ter alguns problemas com a recepção . Foi comentado que "não explica muita coisa além de como confiar em outra pessoa para fazer o trabalho" e o fato de quantas pessoas votaram nesse comentário mostra claramente que muitas coisas precisam de esclarecimentos.
Antes de tudo, uma boa resposta para "Como veicular imagens usando o Node.js" não está implementando um servidor de arquivos estático do zero e fazendo mal. Uma boa resposta é usar um módulo como o Express que faça o trabalho corretamente .
Os comentários de resposta que dizem que o uso do Express "não explica muito além de como confiar em alguém para fazer o trabalho" deve ser observado, que o uso do http
módulo já conta com alguém para fazer o trabalho. Se alguém não quiser confiar em ninguém para fazer o trabalho, pelo menos os soquetes TCP não processados devem ser usados - o que eu faço em um dos meus exemplos abaixo.
Um problema mais sério é que todas as respostas aqui que usam o http
módulo estão quebradas . Eles introduzem condições de corrida , resolução de caminho insegura que levará a vulnerabilidade de percurso , bloqueando a E / S que falhará completamente em atender a solicitações simultâneas de todos e outros problemas sutis - elas são completamente quebradas como exemplos do que a pergunta faz e no entanto, eles já usam a abstração fornecida pelo http
módulo em vez de usar soquetes TCP, para que nem façam tudo do zero, como afirmam.
Se a pergunta era "Como implementar um servidor de arquivos estático a partir do zero, como um exercício de aprendizado", deve-se postar todas as respostas sobre como fazer isso - mas mesmo assim, devemos esperar que pelo menos estejam corretas . Além disso, não é razoável supor que alguém que queira servir uma imagem possa querer servir mais imagens no futuro, portanto, alguém poderia argumentar que escrever um servidor de arquivos estático personalizado específico que pode servir apenas um único arquivo com caminho codificado é um pouco míope. Parece difícil imaginar que qualquer pessoa que procure uma resposta sobre como exibir uma imagem se contentaria com uma solução que serve apenas uma imagem em vez de uma solução geral para servir qualquer imagem.
Em suma, a questão é como servir uma imagem e uma resposta a isso é usar um módulo apropriado para fazê-lo de maneira segura, pré-executável e confiável, que seja legível, sustentável e à prova de futuro enquanto utiliza as melhores práticas do Nó profissional. desenvolvimento. Mas concordo que um ótimo complemento para essa resposta seria mostrar uma maneira de implementar a mesma funcionalidade manualmente, mas infelizmente todas as tentativas de fazer isso falharam até agora. E foi por isso que escrevi alguns novos exemplos.
Após esta breve introdução, aqui estão meus cinco exemplos fazendo o trabalho em 5 níveis diferentes de abstração.
Funcionalidade mínima
Todo exemplo serve arquivos do public
diretório e suporta a funcionalidade mínima de:
- Tipos MIME para os arquivos mais comuns
- serve HTML, JS, CSS, texto sem formatação e imagens
- serve
index.html
como um índice de diretório padrão
- responde com códigos de erro para arquivos ausentes
- sem vulnerabilidades transversais ao caminho
- sem condições de corrida ao ler arquivos
Testei todas as versões nas versões 4, 5, 6 e 7 do Node.
express.static
Esta versão usa o express.static
middleware embutido do express
módulo.
Este exemplo tem mais funcionalidade e menos quantidade de código.
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
Esta versão usa o express
módulo, mas sem o express.static
middleware. A veiculação de arquivos estáticos é implementada como um manipulador de rota única usando fluxos.
Este exemplo possui contramedidas simples de deslocamento de caminho e suporta um conjunto limitado dos tipos MIME mais comuns.
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
Esta versão usa o connect
módulo que é um nível de abstração menor que express
.
Este exemplo tem funcionalidade semelhante à express
versão, mas usando APIs de alavanca um pouco mais baixa.
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
Esta versão usa o http
módulo que é a API de nível mais baixo para HTTP no Nó.
Este exemplo tem funcionalidade semelhante à connect
versão, mas usando APIs de nível ainda mais baixo.
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
Esta versão usa o net
módulo que é a API de nível mais baixo para soquetes TCP no Nó.
Este exemplo possui algumas das funcionalidades da http
versão, mas o protocolo HTTP mínimo e incompleto foi implementado do zero. Como ele não suporta codificação em partes, ele carrega os arquivos na memória antes de servi-los para saber o tamanho antes de enviar uma resposta, pois a definição dos arquivos e o carregamento carregariam uma condição de corrida.
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/');
});
Exemplos de download
Postei todos os exemplos no GitHub com mais explicações.
Exemplos com express.static
, express
, connect
, http
e net
:
Outro projeto usando apenas express.static
:
Testes
Os resultados dos testes estão disponíveis no Travis:
Tudo é testado nas versões 4, 5, 6 e 7 do Node.
Veja também
Outras respostas relacionadas: