Subiendo archivos a AWS-S3 con Nodejs/ExpressJS.
PutObjectCommand vs Upload o Buffer vs Readable?
Si vas empezando en el mundo del desarrollo de sofware o llevas muchooo tiempo en esto como algunos como yo(ya mi esplada es testigo de esto) tendrás que lidear con archivos, subirlos a un servidor, validar que sean correctos y muuuchas otras funcionalidades.
Usaremos ExpressJS para nuestro backend y subiremos nuestros archivos a AWS-S3 mediante el SDK para Javascript que nos proporciona AWS.
El primer paso que debemos saber son las características de los archivos que vamos a subir. Serán archivos que pesan GB? queremos enviar nuestro archivo a S3 en pedacitos o todo de un tiro? Esto nos servirá para saber que estructura de datos vamos a usar, ya sea Buffer o Readable.
Características de Buffer.
Tipo de dato que nos permite trabajar con una cantidad fija de memoria en NodeJS.
Es usado para representar datos binarios (secuencia de bytes), es muy utilizado para manejar datos que provienen de archivos, la red o alguna fuente de stream.
La implementación o clase Buffer nos permite hacer muchas cosas con los datos, convertirlo a string, copiarlos y demás operaciones que pueden ver en la documentación oficial de NodeJS.
Casos de uso para Buffer:
Lectura o escritura de archivos.
Manejo de datos binarios, como imágenes, archivos de video u otros medios.
Comunicación en red (por ejemplo, trabajar con datos TCP/UDP)
const buffer = Buffer.from('Hello, world!', 'utf-8');
console.log(buffer); // <Buffer 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21>
Características de Readable Stream.
Buffer y Readable son muy parecidos en cuanto a su uso. Podemos usarlos para leer y escribir archivos, manejar paquetes de red y la gran diferencia yace en que Readable el manejo de datos se hace por pedazos y en Buffer es en un solo conjunto.
Características:
Datos transmitidos: los flujos legibles manejan los datos en fragmentos, lo que le permite procesarlos a medida que llegan en lugar de esperar a que se carguen todos los datos. Esto los hace más eficientes en términos de memoria para conjuntos de datos grandes.
Datos a on-demand: los datos se pueden leer de un flujo legible ya sea manualmente llamando al método .read() o automáticamente escuchando el evento 'data'.
- Asíncrono: los flujos legibles son asíncronos y son adecuados para manejar grandes cantidades de datos o datos que llegan con el tiempo (por ejemplo, leer un archivo grande o recibir datos a través de la red).
Casos de uso:
Leer archivos grandes (por ejemplo, video, audio) o grandes cantidades de datos que no caben en la memoria a la vez. Manejo de solicitudes y respuestas de red. Transmisión de datos (por ejemplo, video en vivo o transmisión de audio).
const fs = require('fs');
const readableStream = fs.createReadStream('archivoGrande.txt', { encoding: 'utf8' });
readableStream.on('data', (chunk) => {
console.log(`Procesando ${chunk.length} bytes of data.`);
});
readableStream.on('end', () => {
console.log('Terminamos de leer datos.');
});
Una vez que elijamos el tipo de datos que vamos a usar, pasamos a la implementación de AWS, que dependerá si usaremos stream de datos o subiremos todo de un solo golpe.
PutObjectCommand
de la librería @aws-sdk/client-s3
Este enfoque es una forma sencilla y directa de cargar un archivo en S3. PutObjectCommand es parte del cliente básico de S3, que nos permite realizar operaciones básicas en S3 como cargar archivos, enumerar contenedores/buckets, eliminar objetos, etc. Es sencillo y funciona bien para archivos de tamaño pequeño a moderado, por eso usar Buffer con PutObjectCommand puede ser una eficiente combinación si no planeas crear el nuevo Netflix o DropBox.
Casos de uso:
Cuando necesita subir archivos pequeños donde no es necesaria la carga de varias partes o stream.
Si no necesita funciones avanzadas como el seguimiento del progreso de la subida, el manejo de reintentos o la personalización de la concurrencia.
const data = await this.client.send(new PutObjectCommand(params));
Upload
de la librería @aws-sdk/lib-storage
Esta opción utiliza el paquete @aws-sdk/lib-storage, que proporciona herramientas para manejar archivos más avanzadas, en particular para archivos grandes.
Está diseñada para manejar archivos en varias partes de forma automática.
El poder subir datos en partes es un método que nos permite procesar archivos grandes de forma mas eficiente, es útil para el rendimiento y la confiabilidad, especialmente para archivos de más de 5 GB.
Ofrece características adicionales como seguimiento del progreso, control de concurrencia (queueSize) y la capacidad de dejar partes con errores para su manejo manual.
Casos de uso:
Subir archivos grandes por partes.
Manejar el reintento, el progreso y errores de la subida de archivos.
const upload = new Upload({
client: new S3Client({}),
tags: [...],
queueSize: 4,
leavePartsOnError: false,
params: target,
});
await upload.done();
Ejemplo final
import express, { Request, Response } from 'express';
import multer from 'multer';
const app = express();
// Configurar Multer para guardar archivos en memoria
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
// Endpoint q sube el archivo
app.post('/upload', upload.single('file'), async (req: Request, res: Response) => {
if (!req.file) {
return res.status(400).send('No file uploaded.');
}
//El archivo subido es accessible desde req.file
const file = req.file;
// Ejem para subir a s3
const params = {
Bucket: 'your-bucket-name',
Key: file.originalname, //Nombre que se guardara en S3
Body: file.buffer, // Contenido .. buffer puede ser un Readable stream
ContentType: file.mimetype,
};
try {
const result = await s3Client.send(new PutObjectCommand(params));
res.status(200).send(`Archivo subido correctamente. S3 URL: ${result.Location}`);
} catch (error) {
res.status(500).send(`Fallo el proceso. Error: ${error.message}`);
}
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
Espero les sirva y no duden en dejar sus comentarios o suscribirse al newsletter.