multipart/form-data é usado para enviar arquivos e campos no mesmo request. Em Node.js, o parser precisa trabalhar em streaming, aplicar limites antes de acumular dados e tratar cada parte como entrada não confiável.
Por que não usar express.json
JSON não transporta arquivos binários de forma eficiente. Base64 aumenta o tamanho e exige memória. Multipart separa partes e permite processar streams.
Busboy
npm install busboyimport Busboy from 'busboy';
app.post('/uploads', requireAuth, (req, res, next) => {
const busboy = Busboy({
headers: req.headers,
limits: {
files: 1,
fileSize: 10 * 1024 * 1024,
fields: 10,
fieldSize: 16 * 1024,
parts: 20,
},
});
req.pipe(busboy);
});Processando arquivo
busboy.on('file', (name, file, info) => {
const { filename, mimeType } = info;
const id = crypto.randomUUID();
const destination = createPrivateUploadStream(id);
file.on('limit', () => {
destination.destroy(new Error('Arquivo excede o limite'));
});
file.pipe(destination);
});Não use filename como caminho. MimeType é declaração do cliente e precisa ser verificado pelo conteúdo.
Campos
const fields = {};
busboy.on('field', (name, value, info) => {
if (!ALLOWED_FIELDS.has(name)) return;
fields[name] = value;
});Limite tamanho e quantidade. Valide com JSON Schema ou Zod após o parsing.
Ordem das partes
O cliente pode enviar arquivo antes dos campos. Não dependa de ordem. Se uma decisão de autorização depende de um campo, prefira colocá-la na URL, identidade ou uma etapa anterior.
Backpressure
Use pipeline para respeitar o consumidor:
await pipeline(file, transform, destination, { signal });Não processe chunks com listeners que iniciam tarefas ilimitadas.
Finalização
Responda somente depois que parser e streams concluírem. Controle promises de cada arquivo:
const tasks = [];
busboy.on('file', (name, file) => {
tasks.push(storeFile(file));
});
busboy.on('close', async () => {
try {
const uploads = await Promise.all(tasks);
res.status(201).json({ uploads });
} catch (error) {
next(error);
}
});Falhas parciais
Se um de vários arquivos falha, decida se toda a operação é rejeitada. Exclua objetos já gravados ou marque-os como órfãos para limpeza.
Abortamento
Quando o cliente desconecta:
const controller = new AbortController();
req.once('aborted', () => controller.abort());
req.once('close', () => {
if (!res.writableEnded) controller.abort();
});Propague o signal para storage e scanners.
Content-Type
O boundary vem no header. Não construa parser com boundary fornecido em outro campo. Capture erros de header inválido e retorne 400.
Limites em múltiplas camadas
Configure body size no CDN, proxy, ingress e aplicação. Valores incompatíveis geram resets difíceis de diagnosticar.
Upload direto ao object storage
Para arquivos grandes, o backend pode gerar URL pré-assinada. O cliente envia direto e depois confirma metadados. Ainda é preciso validar tamanho, tipo, autorização e status do objeto antes de disponibilizar.
Multipart upload do storage
Não confunda HTTP multipart/form-data com multipart upload de S3. O segundo divide um objeto grande em partes e exige conclusão ou abort para evitar custos de partes abandonadas.
Checksums
Calcule hash durante o stream para integridade e deduplicação controlada:
const hash = crypto.createHash('sha256');
file.on('data', (chunk) => hash.update(chunk));Não use hash como autorização e considere ataques de deduplicação que revelam existência de arquivos.
Campos JSON em multipart
Um campo pode conter JSON, mas limite tamanho e faça parse em try/catch. Em contratos complexos, pode ser melhor criar o recurso em JSON e enviar o arquivo em endpoint separado.
Multer
Multer é popular e pode armazenar em memória ou disco. Evite memoryStorage para arquivos grandes. Configure limits, fileFilter e um storage que gere nomes seguros.
Segurança do parser
Mantenha a biblioteca atualizada. Parsers de multipart enfrentam casos extremos de boundaries, headers e partes malformadas. Teste payloads truncados e muitos campos.
CSRF e CORS
Formulários multipart podem ser enviados cross-site. Se a autenticação usa cookies, exija token CSRF e valide Origin.
Observabilidade
Meça bytes, duração, abortos, limites excedidos, falhas de storage, partes e scanner. Não use filename como label de métrica.
Erros comuns
- bufferizar arquivo inteiro;
- confiar no filename;
- sem limite de fields ou parts;
- responder antes das streams;
- ignorar cliente desconectado;
- não limpar uploads parciais;
- confiar em mimetype;
- memoryStorage em produção;
- depender da ordem;
- sem CSRF.
Fluxo recomendado
- autorize antes do body;
- configure limits;
- processe em streaming;
- gere ID interno;
- valide campos;
- detecte tipo real;
- propague cancelamento;
- aguarde todas as partes;
- compense falhas;
- monitore volume.
Combine Multipart com Upload Seguro de Arquivos, Streams e Backpressure, AbortController, JSON Schema e CSRF.
Consulte a documentação do Busboy e a referência HTTP da MDN.



