Lidar corretamente com uploads de arquivos envolve mais do que apenas aceitar um arquivo – você precisa de validação, estratégia de armazenamento, segurança e, muitas vezes, processamento de imagens. Este guia cobre uploads robustos de arquivos em Node.js, desde armazenamento local simples até uploads escalonáveis na nuvem.
📋 Table of Contents
- Visão geral das estratégias de upload
- Upload Básico com Multer
- Valide os arquivos corretamente (não confie apenas no tipo MIME)
- Vários arquivos e campos
- Processamento de imagem com Sharp
- Upload direto para a nuvem com URLs pré-assinados (melhor para escala)
- Lista de verificação de segurança
- P: Os uploads devem passar pelo meu servidor ou diretamente para o armazenamento na nuvem?
- Lidar com uploads de arquivos em Node.js requer mais do que aceitar um arquivo. Usar
Visão geral das estratégias de upload
| Estratégia | Melhor para |
|---|---|
| Armazenamento em disco local | Aplicativos pequenos, servidor único |
| Faça upload para o seu servidor e depois para a nuvem | Processamento necessário antes do armazenamento |
| Direto para a nuvem (URLs predefinidos) | Arquivos grandes, escala, melhores práticas |
Upload Básico com Multer
npm install multer
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
// Generate a safe, unique filename - never trust the client's name
const unique = Date.now() + '-' + Math.round(Math.random() * 1e9);
cb(null, unique + path.extname(file.originalname));
},
});
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB limit
fileFilter: (req, file, cb) => {
// Only allow specific types
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Only JPEG, PNG, and WebP images allowed'));
}
},
});
app.post('/upload', upload.single('file'), (req, res) => {
res.json({ filename: req.file.filename, size: req.file.size });
});
Valide os arquivos corretamente (não confie apenas no tipo MIME)
npm install file-type
const { fileTypeFromBuffer } = require('file-type');
const fs = require('fs').promises;
// The client-provided MIME type can be faked - verify actual content
async function validateFile(filePath) {
const buffer = await fs.readFile(filePath);
const type = await fileTypeFromBuffer(buffer);
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
if (!type || !allowed.includes(type.mime)) {
await fs.unlink(filePath); // delete the invalid file
throw new Error('Invalid file content');
}
return type;
}
Vários arquivos e campos
// Multiple files under one field
app.post('/gallery', upload.array('photos', 10), (req, res) => {
const files = req.files.map(f => ({ filename: f.filename, size: f.size }));
res.json({ uploaded: files });
});
// Different fields
const uploadFields = upload.fields([
{ name: 'avatar', maxCount: 1 },
{ name: 'documents', maxCount: 5 },
]);
app.post('/profile', uploadFields, (req, res) => {
const avatar = req.files.avatar?.[0];
const docs = req.files.documents || [];
res.json({ avatar: avatar?.filename, docCount: docs.length });
});
Processamento de imagem com Sharp
npm install sharp
const sharp = require('sharp');
// Resize and optimize uploaded images
async function processImage(inputPath, outputPath) {
await sharp(inputPath)
.resize(1200, 1200, { fit: 'inside', withoutEnlargement: true })
.webp({ quality: 80 }) // convert to WebP, compress
.toFile(outputPath);
}
// Generate a thumbnail
async function makeThumbnail(inputPath, thumbPath) {
await sharp(inputPath)
.resize(200, 200, { fit: 'cover' })
.toFile(thumbPath);
}
Upload direto para a nuvem com URLs pré-assinados (melhor para escala)
Para arquivos grandes e em grande escala, não roteie uploads através do seu servidor — deixe os clientes fazerem upload diretamente para o S3 usando um URL predefinido gerado pelo seu back-end:
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const s3 = new S3Client({ region: 'us-east-1' });
// Backend: generate a presigned upload URL
app.post('/upload-url', async (req, res) => {
const { filename, contentType } = req.body;
const key = `uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: 'my-bucket',
Key: key,
ContentType: contentType,
});
const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
res.json({ uploadUrl, key });
});
// Frontend: upload directly to S3, bypassing your server
const { uploadUrl, key } = await fetch('/upload-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: file.name, contentType: file.type }),
}).then(r => r.json());
await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': file.type },
body: file, // uploads straight to S3 - no server bottleneck
});
Lista de verificação de segurança
- Validar tipo de arquivo por conteúdo, não apenas o tipo MIME do cliente (que pode ser falsificado)
- Limitar o tamanho do arquivo em todas as camadas (multer, nginx, cloud)
- Gere nomes de arquivos seguros — nunca use o nome do arquivo do cliente diretamente (risco de passagem do caminho)
- Armazenar fora da raiz da web ou em armazenamento em nuvem, não em um diretório público que possa executar uploads
- Procurar malware se estiver aceitando arquivos de usuários não confiáveis Defina o tipo de conteúdo e a disposição de conteúdo adequados
- ao enviar arquivos de voltaPerguntas Frequentes
P: Os uploads devem passar pelo meu servidor ou diretamente para o armazenamento na nuvem?
R: Para arquivos pequenos ou quando você precisar processá-los primeiro, através do seu servidor (multer). Para arquivos grandes e em escala, use URLs pré-assinados para que os clientes façam upload diretamente para o S3 — isso contorna a largura de banda e os limites de tamanho do servidor e escala muito melhor.
P: Por que não devo confiar no tipo MIME do cliente?
R: O cabeçalho Content-Type é controlado pelo cliente e facilmente falsificado — um invasor pode fazer upload de um arquivo malicioso rotulado como uma imagem. Verifique o conteúdo real do arquivo com uma biblioteca como file-type, que lê os bytes mágicos do arquivo.
P: Como lidar com uploads de arquivos muito grandes?
R: Use URLs predefinidos para upload direto para a nuvem (sem gargalos no servidor) ou implemente uploads em partes/recuperáveis para arquivos grandes. Aumente os limites de tamanho em todas as camadas (multer, nginx, cloud) se rotear através do seu servidor, mas direto para a nuvem é melhor para arquivos grandes.
P: Onde devo armazenar os arquivos enviados?
R: Armazenamento de objetos em nuvem (S3, GCS, R2) para escalabilidade e confiabilidade — não no disco do servidor de aplicativos (que não é dimensionado entre instâncias e é perdido se o servidor for substituído). Para aplicativos pequenos de servidor único, o disco local funciona, mas planeje migrar para o armazenamento em nuvem à medida que você cresce.
P: Como evito que os arquivos enviados sejam executados?
R: Armazene uploads fora da raiz da web ou no armazenamento em nuvem, gere nomes de arquivos aleatórios, valide o tipo de conteúdo e forneça arquivos com cabeçalhos adequados (Disposição de conteúdo: anexo para downloads). Nunca armazene uploads de usuários em um diretório onde o servidor web os executaria como scripts.
Conclusão
Lidar com uploads de arquivos em Node.js requer mais do que aceitar um arquivo. Usar
multer para uploads do lado do servidor com limites de tamanho e filtragem de tipo, validar arquivos por conteúdo real (não o tipo MIME do cliente) e processar imagens com Sharp. Para arquivos grandes e em escala, useURLs predefinidos para permitir que os clientes façam upload diretamente para o armazenamento em nuvem, ignorando seu servidor. Sempre siga as práticas de segurança: valide conteúdo, limite tamanhos, gere nomes de arquivos seguros e armazene arquivos em armazenamento em nuvem ou fora da raiz da web. Fazer uploads corretamente significa equilibrar conveniência, desempenho e segurança. Direto para a nuvem com validação adequada é a prática recomendada moderna para a maioria dos aplicativos., ignorando seu servidor. Sempre siga as práticas de segurança: valide conteúdo, limite tamanhos, gere nomes de arquivos seguros e armazene arquivos em armazenamento em nuvem ou fora da raiz da web. Fazer uploads corretamente significa equilibrar conveniência, desempenho e segurança. Direto para a nuvem com validação adequada é a prática recomendada moderna para a maioria dos aplicativos.
🔗 Share this article
✍️ Leave a Comment