Enviando Arquivos - Node.js
O envio de arquivos estáticos é uma tarefa bastante comum na construção e operação de uma aplicação web. Vamos analisar como enviar arquivos em uma aplicação Node.js.
Suponha que temos três arquivos no diretório do projeto:
app.js
about.html
index.html
O arquivo app.js
index.html
<!DOCTYPE html>
<html>
<head>
<title>Principal</title>
<meta charset="utf-8" />
</head>
<body>
<h1>Principal</h1>
</body>
<html>
De forma semelhante, definimos o código no arquivo about.html
<!DOCTYPE html>
<html>
<head>
<title>Sobre</title>
<meta charset="utf-8" />
</head>
<body>
<h1>Sobre</h1>
</body>
<html>
Nossa tarefa será enviar o conteúdo desses arquivos para o usuário.
Primeiro Método
Para ler um arquivo, podemos usar o método fs.createReadStream()
pipe()
app.js
const http = require("http");
const fs = require("fs");
http.createServer(function(request, response) {
console.log(`Endereço solicitado: ${request.url}`);
// obtém o caminho após a barra
const filePath = request.url.substring(1);
// verifica se o arquivo existe
fs.access(filePath, fs.constants.R_OK, err => {
// se ocorrer um erro, envia o código de status 404
if(err) {
response.statusCode = 404;
response.end("Recurso não encontrado!");
} else {
fs.createReadStream(filePath).pipe(response);
}
});
}).listen(3000, function() {
console.log("Servidor iniciado na porta 3000");
});
Primeiramente, obtemos o endereço solicitado. Suponha que o endereço solicitado corresponda diretamente ao caminho do arquivo no servidor. Em seguida, com a função assíncrona fs.access
fs.constants.R_OK
Para enviar o arquivo, aplicamos a cadeia de métodos:
fs.createReadStream(filePath).pipe(response);
O método fs.createReadStream(filePath)
fs.ReadStream
pipe()
stream.Writable
http.ServerResponse
Vamos iniciar a aplicação e acessar no navegador o endereço http://localhost:3000/index.html

De forma semelhante, podemos acessar o endereço http://localhost:3000/about.html

Nesse caso, enviamos arquivos HTML, mas de maneira similar podemos enviar diferentes tipos de arquivos. Por exemplo, definimos no projeto a pasta public
styles.css
body {
font-family: Verdana;
color: navy;
}
Aplicamos esses estilos na página index.html
<!DOCTYPE html>
<html>
<head>
<title>Principal</title>
<meta charset="utf-8" />
<link href="public/styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>Principal</h1>
</body>
<html>
E então acessamos index.html

Segundo Método
O segundo método envolve a leitura dos dados usando a função fs.readFile()
response.end()
const http = require("http");
const fs = require("fs");
http.createServer(function(request, response) {
console.log(`Endereço solicitado: ${request.url}`);
// obtém o caminho após a barra
const filePath = request.url.substring(1);
fs.readFile(filePath, function(error, data) {
if(error) {
response.statusCode = 404;
response.end("Recurso não encontrado!");
} else {
response.end(data);
}
});
}).listen(3000, function(){
console.log("Servidor iniciado na porta 3000");
});
O resultado será similar, porém existem algumas diferenças:
fs.createReadStream
Fluxo de Dados (Streaming): fs.createReadStream cria um fluxo de leitura para o arquivo, permitindo que ele seja lido em partes (chunks) e transmitido diretamente para o cliente. Isso é útil para arquivos grandes, pois não há necessidade de carregar o arquivo inteiro na memória antes de enviá-lo.
Uso de Memória: Como os dados são lidos e enviados em partes, o uso de memória é mais eficiente, o que é ideal para servidores que lidam com muitos usuários ou arquivos grandes.
Desempenho: A leitura em fluxo pode ser mais rápida para grandes arquivos, pois começa a enviar os dados imediatamente, sem esperar que todo o arquivo seja lido.
fs.readFile
Leitura Completa: fs.readFile lê o arquivo inteiro na memória antes de enviar os dados para o cliente. Isso pode ser mais simples de implementar para arquivos pequenos, mas se torna ineficiente para arquivos grandes.
Uso de Memória: Requer que o arquivo inteiro seja carregado na memória. Para arquivos grandes ou em sistemas com recursos limitados, isso pode levar a problemas de memória.
Desempenho: Pode ser mais lento para arquivos grandes, pois precisa ler todo o arquivo antes de começar a enviá-lo. No entanto, para arquivos pequenos, a diferença de desempenho pode ser insignificante.