Como desenvolver uma aplicação Node.js para upload direto de arquivos via web para um bucket S3

Diego Norman
5 min readJan 28, 2025

Passo 1: Configuração do Projeto

2. Crie uma nova pasta para o projeto:

mkdir node-s3-upload
cd node-s3-upload

2. Inicialize o projeto Node.js:

npm init -y

3. Instale as dependências necessárias:

npm install express cors multer @aws-sdk/client-s3 @aws-sdk/lib-storage dotenv

4. Instale o nodemon para desenvolvimento (opcional):

npm install — save-dev nodemon

5. Crie o arquivo .env:

Crie um arquivo .env na raiz do projeto e adicione as credenciais da AWS:

AWS_ACCESS_KEY_ID=acesskey
AWS_SECRET_ACCESS_KEY=secretkey
AWS_REGION=region
S3_BUCKET_NAME=nomedobucket
PORT=3000

Passo 2: Configuração do Servidor

Crie o arquivo server.js:


require('dotenv').config();
const express = require('express');
const cors = require('cors');
const multer = require('multer');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { Upload } = require('@aws-sdk/lib-storage');

const app = express();
const port = process.env.PORT || 3000;

// Configuração do AWS SDK v3
const s3Client = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
});

// Configuração do Multer para armazenar arquivos localmente antes do upload
const storage = multer.memoryStorage();
const upload = multer({ storage });

// Função para realizar o upload no S3
async function uploadToS3(file) {
const key = `${Date.now()}-${file.originalname}`;
const upload = new Upload({
client: s3Client,
params: {
Bucket: process.env.S3_BUCKET_NAME,
Key: key, // Nome do arquivo no S3
Body: file.buffer, // Arquivo em buffer da memória
ContentType: file.mimetype, // Define o tipo MIME
},
});

await upload.done(); // Executa o upload
const fileUrl = `https://${process.env.S3_BUCKET_NAME}.s3.${process.env.AWS_REGION}.amazonaws.com/${key}`;

// Log detalhado para verificar o upload bem-sucedido
console.log(`Arquivo enviado com sucesso: ${file.originalname}`);
console.log(`URL do arquivo: ${fileUrl}`);

return fileUrl;
}

// Middleware para permitir CORS e JSON
app.use(cors());
app.use(express.json());

// Rota para upload de arquivos
app.post('/upload', upload.array('files', 10), async (req, res) => {
try {
const fileUrls = await Promise.all(
req.files.map(file => {
console.log(`Iniciando upload do arquivo: ${file.originalname}`);
return uploadToS3(file);
})
);
console.log('Todos os arquivos foram enviados com sucesso!');
res.status(200).json({ message: 'Upload realizado com sucesso!', fileUrls });
} catch (error) {
console.error('Erro ao fazer upload:', error);
res.status(500).json({ error: 'Erro ao fazer upload dos arquivos.' });
}
});

// Iniciar o servidor
app.use(express.static('public'));
app.listen(port, () => {
console.log(`Servidor rodando na porta ${port}`);
});

Passo 3: Criar a Interface de Arrastar e Soltar

  1. Crie uma pasta public na raiz do projeto:
mkdir public

2. Crie o arquivo index.html dentro da pasta public:

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload de Arquivos para S3</title>
<style>
/* Estilos para o tema dark */
body {
font-family: 'Arial', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 0;
padding: 20px;
background-color: #121212;
color: #fff;
}
h1 {
font-size: 2rem;
color: #fff;
margin-bottom: 30px;
}
/* Box de upload */
.upload-box {
border: 2px dashed #bbb;
padding: 20px;
text-align: center;
width: 300px;
background-color: #1f1f1f;
border-radius: 10px;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.upload-box.dragover {
border-color: #00c9ff;
background-color: #333;
}
#file-input {
display: none;
}
.upload-button {
background-color: #007bff;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.upload-button:hover {
background-color: #0056b3;
}
/* Galeria de imagens */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 10px;
width: 100%;
max-width: 800px;
}
.gallery-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid #444;
border-radius: 5px;
padding: 10px;
background-color: #222;
}
.gallery-item img {
max-width: 100%;
max-height: 100px;
margin-bottom: 5px;
border-radius: 5px;
}
.gallery-item p {
word-wrap: break-word;
font-size: 14px;
text-align: center;
color: #bbb;
}
.file-link {
margin-top: 5px;
color: #00c9ff;
text-decoration: none;
font-size: 12px;
word-wrap: break-word;
}
/* Logo da AWS */
.aws-logo {
margin-top: 30px;
max-width: 200px;
}
</style>
</head>
<!-- Logo da AWS centralizada -->
<img src="https://logos-world.net/wp-content/uploads/2021/08/Amazon-Web-Services-AWS-Logo.png" alt="AWS Logo" class="aws-logo">

<body>
<h1>Upload de Arquivos para o S3</h1>
<div class="upload-box" id="upload-box">
<p>Arraste e solte arquivos aqui ou clique para selecionar</p>
<input type="file" id="file-input" multiple accept=".jpg,.jpeg,.png,.pdf,.doc,.docx,.xls,.xlsx">
<button class="upload-button" onclick="document.getElementById('file-input').click()">Selecionar Arquivos</button>
</div>
<div class="gallery" id="gallery"></div>

<script>
const uploadBox = document.getElementById('upload-box');
const fileInput = document.getElementById('file-input');
const gallery = document.getElementById('gallery');

uploadBox.addEventListener('dragover', (e) => {
e.preventDefault();
uploadBox.classList.add('dragover');
});

uploadBox.addEventListener('dragleave', () => {
uploadBox.classList.remove('dragover');
});

uploadBox.addEventListener('drop', (e) => {
e.preventDefault();
uploadBox.classList.remove('dragover');
const files = e.dataTransfer.files;
handleFiles(files);
});

fileInput.addEventListener('change', (e) => {
const files = e.target.files;
handleFiles(files);
});

async function handleFiles(files) {
const formData = new FormData();
gallery.innerHTML = ''; // Limpa a galeria antes de adicionar novos arquivos

for (const file of files) {
formData.append('files', file);

// Adiciona à galeria
const galleryItem = document.createElement('div');
galleryItem.classList.add('gallery-item');

if (file.type.startsWith('image/')) {
const img = document.createElement('img');
img.src = URL.createObjectURL(file);
img.onload = () => URL.revokeObjectURL(img.src); // Libera memória
galleryItem.appendChild(img);
} else {
const placeholder = document.createElement('p');
placeholder.textContent = file.name;
galleryItem.appendChild(placeholder);
}

gallery.appendChild(galleryItem);
}

try {
const response = await fetch('/upload', {
method: 'POST',
body: formData,
});
const result = await response.json();

// Exibe os links de acesso externo para os arquivos
result.fileUrls.forEach((url, index) => {
const galleryItem = gallery.children[index];
const link = document.createElement('a');
link.href = url;
link.textContent = 'Acessar Arquivo';
link.target = '_blank';
link.classList.add('file-link');
galleryItem.appendChild(link);
});

alert(result.message);
console.log('URLs dos arquivos:', result.fileUrls);
} catch (error) {
console.error('Erro ao fazer upload:', error);
alert('Erro ao fazer upload dos arquivos.');
}
}
</script>
</body>
</html>

Passo 4: Executar o Projeto

  1. Inicie o servidor:
node server.js
Alternativas: - npm start
- node server.js
  1. Acesse a aplicação:
    Abra o navegador e acesse http://localhost:3000. Você verá a interface de arrastar e soltar. Faça o upload de arquivos e eles serão enviados diretamente para o bucket S3.

Conclusão do projeto:

O bucket continua 100% privado, você poderá fazer isso de forma publica.
Este projeto foi criado, containerizado e implantado de forma segura, com variáveis utilizando CI/CD com GitLab.
Caso deseje o fluxo completo, entre em contato conosco."

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Diego Norman
Diego Norman

Written by Diego Norman

Como um Arquiteto de Infraestrutura Cloud, trago uma paixão inabalável pela tecnologia.

Responses (1)

Write a response

Informações valiosas, parabéns Diego!