Express: File System
Descubre cómo gestionar archivos y directorios en Express con file system para crear aplicaciones web dinámicas y seguras.
Aprende Express GRATIS y certifícateFile System en Express
El file system es uno de los aspectos fundamentales en el desarrollo de aplicaciones web con Express. La capacidad de interactuar con archivos y directorios del servidor permite crear aplicaciones dinámicas que pueden leer configuraciones, servir contenido estático, procesar uploads de usuarios y gestionar datos persistentes.
Express proporciona múltiples mecanismos para trabajar con el sistema de archivos, desde el middleware integrado para servir archivos estáticos hasta la integración con las APIs nativas de Node.js para operaciones más complejas. Esta funcionalidad es esencial para construir aplicaciones web completas que van más allá del simple procesamiento de requests HTTP.
Servicio de archivos estáticos
El middleware estático de Express permite servir archivos directamente desde el sistema de archivos sin necesidad de crear rutas específicas para cada archivo. Esta funcionalidad es fundamental para servir CSS, JavaScript, imágenes y otros recursos que las aplicaciones web necesitan.
import express from 'express';
import path from 'path';
const app = express();
// Servir archivos estáticos desde el directorio 'public'
app.use(express.static('public'));
// Servir archivos con un prefijo de ruta
app.use('/assets', express.static('static'));
La configuración básica permite que los archivos en el directorio especificado sean accesibles directamente a través de URLs. Por ejemplo, un archivo public/styles.css
estará disponible en http://localhost:3000/styles.css
.
Configuración avanzada de archivos estáticos
Express permite personalizar el comportamiento del middleware estático mediante opciones que controlan aspectos como el caching, la seguridad y el rendimiento.
const options = {
dotfiles: 'ignore',
etag: false,
extensions: ['htm', 'html'],
index: false,
maxAge: '1d',
redirect: false,
setHeaders: (res, path, stat) => {
res.set('x-timestamp', Date.now());
}
};
app.use(express.static('public', options));
Estas opciones permiten controlar aspectos críticos como qué archivos pueden ser servidos, cómo se manejan las extensiones de archivo y qué headers HTTP se incluyen en las respuestas.
Lectura de archivos del sistema
Para operaciones más específicas, Express se integra naturalmente con el módulo fs de Node.js, permitiendo leer archivos de forma síncrona y asíncrona según las necesidades de la aplicación.
import fs from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
app.get('/config', async (req, res) => {
try {
const configPath = join(__dirname, 'config', 'app.json');
const configData = await fs.readFile(configPath, 'utf8');
const config = JSON.parse(configData);
res.json(config);
} catch (error) {
res.status(500).json({ error: 'Error al leer configuración' });
}
});
Este enfoque permite cargar dinámicamente archivos de configuración, plantillas o cualquier otro contenido que la aplicación necesite procesar antes de enviar una respuesta.
Gestión de rutas de archivos
El manejo correcto de rutas de archivos es crucial para la portabilidad y seguridad de las aplicaciones Express. El módulo path
de Node.js proporciona herramientas para construir rutas de forma segura y compatible entre sistemas operativos.
import path from 'path';
app.get('/download/:filename', (req, res) => {
const filename = req.params.filename;
// Validar el nombre del archivo para prevenir path traversal
if (filename.includes('..') || filename.includes('/')) {
return res.status(400).json({ error: 'Nombre de archivo inválido' });
}
const filePath = path.join(__dirname, 'downloads', filename);
// Verificar que el archivo existe antes de enviarlo
fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) {
return res.status(404).json({ error: 'Archivo no encontrado' });
}
res.download(filePath);
});
});
La validación de rutas es esencial para prevenir vulnerabilidades de seguridad como el path traversal, donde usuarios malintencionados podrían acceder a archivos fuera del directorio permitido.
Streaming de archivos
Para archivos grandes, el streaming ofrece mejor rendimiento y menor uso de memoria comparado con cargar todo el archivo en memoria antes de enviarlo.
import { createReadStream } from 'fs';
app.get('/video/:filename', (req, res) => {
const filename = req.params.filename;
const videoPath = path.join(__dirname, 'videos', filename);
const stat = fs.statSync(videoPath);
const fileSize = stat.size;
const range = req.headers.range;
if (range) {
// Soporte para streaming parcial (útil para video/audio)
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
const chunksize = (end - start) + 1;
const file = createReadStream(videoPath, { start, end });
const head = {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunksize,
'Content-Type': 'video/mp4',
};
res.writeHead(206, head);
file.pipe(res);
} else {
const head = {
'Content-Length': fileSize,
'Content-Type': 'video/mp4',
};
res.writeHead(200, head);
createReadStream(videoPath).pipe(res);
}
});
Este enfoque permite reproducir contenido multimedia de forma eficiente, soportando características como la búsqueda en video y la carga progresiva.
Operaciones de escritura
Express también facilita la escritura de archivos, útil para generar reportes, logs personalizados o guardar datos procesados.
app.post('/generate-report', async (req, res) => {
try {
const reportData = req.body;
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `report-${timestamp}.json`;
const reportPath = path.join(__dirname, 'reports', filename);
await fs.writeFile(reportPath, JSON.stringify(reportData, null, 2));
res.json({
message: 'Reporte generado exitosamente',
filename: filename,
downloadUrl: `/download-report/${filename}`
});
} catch (error) {
res.status(500).json({ error: 'Error al generar reporte' });
}
});
La generación dinámica de archivos permite crear contenido personalizado basado en las requests de los usuarios, manteniendo un registro persistente de la información procesada.
Lecciones de este módulo de Express
Lecciones de programación del módulo File System del curso de Express.