Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Uploads para S3 no Node.js

Atualizado em: 31 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

Fazer uploads para S3 no Node.js permite armazenar imagens, documentos, backups e arquivos grandes fora do servidor da aplicação. Object storage oferece durabilidade, escalabilidade, versionamento, políticas de acesso e integração com CDN.

O fluxo seguro não consiste apenas em chamar PutObject. A aplicação precisa gerar keys não previsíveis, limitar tamanho, validar tipo, usar credenciais de workload, evitar buckets públicos, configurar criptografia, tratar multipart uploads, limpar objetos órfãos e controlar downloads.

Neste guia, você aprenderá a configurar o AWS SDK v3, enviar streams, criar URLs pré-assinadas, validar uploads diretos, aplicar políticas, usar multipart, proteger metadados, processar arquivos e testar o fluxo.

O que é Amazon S3?

O Amazon S3 armazena objetos em buckets. A documentação de exemplos do AWS SDK para JavaScript v3 mostra operações como PutObject, GetObject e multipart. A documentação de segurança do S3 reúne práticas de acesso e proteção.

Objeto

Um objeto possui:

  • bucket;
  • key;
  • conteúdo;
  • metadados;
  • tipo;
  • ETag;
  • versão, quando habilitada.

Instalando o SDK

npm install @aws-sdk/client-s3 \
  @aws-sdk/s3-request-presigner

O SDK v3 usa pacotes modulares e commands.

Criando o cliente

import { S3Client } from '@aws-sdk/client-s3';

const s3 = new S3Client({
  region: process.env.AWS_REGION
});

Quando executado na AWS, o SDK pode usar a identidade do workload. Evite colocar access key e secret no código.

Credenciais

Use IAM role, task role, service account ou mecanismo equivalente. Consulte Gestão de Segredos no Node.js.

Privilégio mínimo

A role da API pode precisar apenas de:

  • s3:PutObject em um prefixo;
  • s3:GetObject quando necessário;
  • s3:AbortMultipartUpload;
  • operações multipart específicas.

Ela não precisa listar ou excluir todo o bucket.

Key do objeto

tenants/{tenantId}/uploads/{uuid}

Use IDs gerados pelo servidor. Não use o nome original como caminho.

Path traversal

S3 não possui diretórios reais, mas keys com ../ podem confundir aplicações e políticas. Construa a key com segmentos controlados.

Nome original

Guarde como metadado no banco, depois de limitar tamanho e remover caracteres problemáticos. Escape ao mostrar na interface.

Upload pelo servidor

import { PutObjectCommand } from '@aws-sdk/client-s3';

await s3.send(new PutObjectCommand({
  Bucket: bucket,
  Key: key,
  Body: fileStream,
  ContentType: detectedMimeType,
  Metadata: {
    tenant: tenantId,
    upload: uploadId
  }
}));

Prefira stream para não carregar arquivos inteiros em memória.

Backpressure

Streams permitem que leitura e envio avancem no ritmo da rede. Consulte Streams no Node.js.

Limite de tamanho

Defina limite no parser, na rota e na política do upload. Não confie apenas no header Content-Length.

Contagem real

Conte bytes durante o stream e interrompa quando exceder o máximo.

Content-Type

O tipo enviado pelo cliente não é confiável. Detecte assinatura do arquivo quando a segurança depender do formato.

Extensão

A extensão é apenas parte do nome. Um arquivo .jpg pode conter HTML ou executável.

Uploads diretos

Para arquivos grandes, o frontend pode enviar diretamente ao S3 com URL pré-assinada. A API não precisa transportar todos os bytes.

Gerando URL pré-assinada

import { PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const command = new PutObjectCommand({
  Bucket: bucket,
  Key: key,
  ContentType: expectedContentType
});

const url = await getSignedUrl(s3, command, {
  expiresIn: 300
});

Prazo curto

Use poucos minutos. Uma URL vazada permanece válida até expirar.

Key criada pelo servidor

O cliente não deve escolher livremente bucket e key. A API cria um upload pendente e retorna o destino autorizado.

Registro de upload

CREATE TABLE uploads (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  object_key TEXT NOT NULL UNIQUE,
  status TEXT NOT NULL,
  expected_size BIGINT,
  content_type TEXT,
  created_at TIMESTAMPTZ NOT NULL,
  expires_at TIMESTAMPTZ NOT NULL
);

Confirmação

Depois do envio, o cliente chama uma rota de confirmação. A API usa HeadObject para conferir existência, tamanho e metadados.

Não confie no callback do cliente

O cliente pode afirmar que enviou um arquivo diferente. Consulte o S3 e compare com o registro pendente.

Presigned POST

Presigned POST permite impor condições como tamanho, prefixo e tipo. É útil quando o navegador usa formulário multipart diretamente com S3.

Condições

Defina:

  • bucket fixo;
  • prefixo;
  • limite de tamanho;
  • Content-Type esperado;
  • prazo;
  • metadados obrigatórios.

CORS do bucket

Uploads diretos do navegador exigem CORS restrito às origens necessárias. Não use * com métodos e headers amplos sem necessidade.

Bucket privado

Mantenha Block Public Access habilitado. Entregue downloads por URL assinada, CDN privada ou endpoint autorizado.

Download autorizado

const command = new GetObjectCommand({
  Bucket: bucket,
  Key: objectKey
});

const url = await getSignedUrl(s3, command, {
  expiresIn: 60
});

Verifique tenant e permission antes de gerar a URL.

Content-Disposition

Para downloads, defina nome seguro:

ResponseContentDisposition:
  'attachment; filename="documento.pdf"'

Sanitize o nome e evite quebra de header.

Arquivos públicos

Se imagens precisam ser públicas, prefira CDN com origin privado e políticas controladas, em vez de tornar todo o bucket público.

Cache-Control

Objetos imutáveis com key versionada podem usar cache longo. Arquivos privados não devem receber política pública.

Criptografia

S3 oferece criptografia no servidor. Para chaves controladas por KMS, configure a key e permissões apropriadas.

KMS

A role precisa de acesso à chave apenas para o prefixo e operação necessários. Monitore erros de throttling e permissão.

Criptografia no cliente

Dados extremamente sensíveis podem ser cifrados antes do upload. Isso exige gerenciamento de chaves, metadados e recuperação.

Versionamento

Habilitar versioning protege contra sobrescrita e exclusão acidental, mas aumenta armazenamento. Defina lifecycle para versões antigas.

Object Lock

Backups e auditorias podem usar retenção imutável. Configure com requisitos legais e operacionais claros.

Multipart upload

Arquivos grandes podem ser divididos em partes. O fluxo cria o upload, envia partes e completa.

Vantagens

  • retomar falhas;
  • paralelizar partes;
  • evitar reiniciar o arquivo;
  • suportar objetos grandes.

Partes paralelas

Limite concorrência. Enviar dezenas de partes ao mesmo tempo pode saturar memória, sockets e banda.

ETag das partes

Guarde o ETag retornado para completar o multipart. A ordem e o número das partes precisam corresponder.

AbortMultipartUpload

Se o fluxo falha, aborte. Uploads incompletos consomem armazenamento.

Lifecycle para incompletos

Configure regra para excluir multipart uploads abandonados após alguns dias.

Retry

O SDK aplica estratégias, mas operações precisam ser idempotentes. Repetir PutObject na mesma key sobrescreve o objeto.

Key imutável

Use key única por upload para evitar sobrescrita acidental. Alterações criam novo objeto e atualizam a referência no banco.

Checksums

Use checksum suportado para verificar integridade em trânsito. ETag não é sempre MD5, especialmente em multipart e criptografia.

Antivírus

Arquivos recebidos podem ser colocados em prefixo de quarentena. Um worker analisa e move ou marca como aprovado.

Não servir antes da análise

O status do banco deve impedir download enquanto o arquivo estiver pendente.

Processamento de imagem

Reencode imagens, limite dimensões e remova metadados quando necessário. Execute bibliotecas nativas em ambiente isolado.

Arquivos HTML e SVG

Podem executar scripts quando servidos inline. Use attachment, domínio separado e políticas de Content-Type.

SSRF

Se a aplicação importa arquivos por URL, aplique as defesas de SSRF no Node.js.

Multi-tenancy

Inclua tenant no prefixo e nas políticas. O banco deve localizar objeto por tenant e ID, não por key recebida do usuário.

Veja Multi-Tenancy no Node.js.

Auditoria

Registre criação, confirmação, análise, download, exclusão e mudança de retenção. Consulte Logs de Auditoria no Node.js.

Logs

Registre bucket lógico, key parcial, upload ID, tamanho, duração e request ID. Não registre URL pré-assinada completa.

Consulte Logs com Pino no Node.js.

Métricas

Monitore bytes enviados, duração, falhas, multipart incompletos, objetos em quarentena, erros de KMS e URLs geradas.

Rate limiting e quotas

Limite uploads por tenant, quantidade, tamanho diário e armazenamento total. Consulte Rate Limiting no Node.js.

Custos

Considere armazenamento, requests, transferência, KMS, lifecycle e CDN. Um upload abusivo pode gerar custo mesmo sem derrubar a API.

Limpeza de órfãos

Compare uploads pendentes e objetos sem referência. Exclua somente após janela e auditoria.

Exclusão

Apagar o registro do banco e esquecer o S3 gera vazamento e custo. Use job idempotente e status de exclusão.

Backup

S3 não substitui automaticamente backup e retenção. Versionamento, replicação e Object Lock atendem necessidades diferentes.

Teste com emulador

MinIO ou LocalStack ajudam no desenvolvimento, mas mantenha testes contra o serviço real para políticas, KMS e presigned URLs.

Teste de upload direto

Gere a URL, envie o arquivo, confirme HeadObject e valide o registro.

Teste de tamanho

Tente enviar acima do limite. A política e a aplicação devem rejeitar.

Teste de tenant

Um usuário do tenant A não deve gerar download para key do tenant B.

Teste de Content-Type

Envie HTML com extensão de imagem e confirme quarentena ou rejeição.

Teste de URL expirada

Use relógio controlado e confirme que a URL não funciona depois do prazo.

Teste multipart

Interrompa após algumas partes, retome e confirme conclusão. Depois teste abort e lifecycle.

Erros comuns

  • Bucket público: objetos ficam expostos.
  • Key com nome do usuário: colisão e path confuso.
  • Credencial estática: rotação e vazamento ficam difíceis.
  • Confiar no Content-Type: arquivo malicioso passa.
  • URL longa: credencial temporária vaza.
  • Multipart sem abort: armazenamento cresce.
  • Sem confirmação: banco aponta para objeto inexistente.

Boas práticas

  • Use identidade de workload.
  • Mantenha bucket privado.
  • Gere keys no servidor.
  • Use streams.
  • Limite tamanho e tipo.
  • Prefira URL curta.
  • Valide HeadObject.
  • Use quarentena.
  • Aborte multipart incompleto.
  • Audite downloads e exclusões.

Conclusão

Fazer uploads para S3 no Node.js permite separar arquivos do servidor da aplicação e escalar armazenamento. O desenho seguro começa com keys controladas, bucket privado e identidade de workload.

URLs pré-assinadas, multipart, checksums e lifecycle completam o fluxo. Com validação, quarentena, autorização por tenant e limpeza de órfãos, o object storage recebe arquivos sem transformar uploads em uma fonte de vazamento, execução maliciosa ou custo invisível.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita