Node
Tutorial Node: Módulo fs
Aprende a usar el módulo fs de Node.js para leer y escribir archivos con métodos asíncronos y síncronos de forma eficiente y segura.
Aprende Node y certifícatereadFile y writeFile
El módulo fs de Node.js proporciona dos métodos fundamentales para trabajar con archivos: readFile
para leer contenido y writeFile
para escribir datos. Estos métodos representan las operaciones más comunes que realizarás al manipular archivos en tus aplicaciones.
Ambos métodos están disponibles en versiones asíncronas y síncronas, lo que te permite elegir el enfoque más adecuado según las necesidades de tu aplicación. La versión asíncrona es la recomendada para la mayoría de casos de uso, ya que no bloquea el hilo principal de ejecución.
Lectura de archivos con readFile
El método readFile
te permite leer el contenido completo de un archivo de forma asíncrona. Su sintaxis básica requiere la ruta del archivo y una función callback que manejará el resultado:
const fs = require('fs');
// Lectura básica de un archivo
fs.readFile('datos.txt', 'utf8', (error, contenido) => {
if (error) {
console.error('Error al leer el archivo:', error.message);
return;
}
console.log('Contenido del archivo:');
console.log(contenido);
});
El segundo parámetro especifica la codificación del archivo. Al usar 'utf8'
, el contenido se devuelve como string. Si omites este parámetro, obtendrás un Buffer con los datos binarios:
// Lectura sin especificar codificación (devuelve Buffer)
fs.readFile('imagen.jpg', (error, buffer) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('Tamaño del archivo:', buffer.length, 'bytes');
console.log('Tipo de dato:', typeof buffer); // object (Buffer)
});
Para manejar errores de forma robusta, siempre verifica la existencia del error antes de procesar el contenido:
const fs = require('fs');
function leerConfiguracion(rutaArchivo) {
fs.readFile(rutaArchivo, 'utf8', (error, datos) => {
if (error) {
if (error.code === 'ENOENT') {
console.log('El archivo de configuración no existe');
} else if (error.code === 'EACCES') {
console.log('Sin permisos para leer el archivo');
} else {
console.log('Error desconocido:', error.message);
}
return;
}
try {
const configuracion = JSON.parse(datos);
console.log('Configuración cargada:', configuracion);
} catch (parseError) {
console.error('Error al parsear JSON:', parseError.message);
}
});
}
leerConfiguracion('./config.json');
Escritura de archivos con writeFile
El método writeFile
te permite crear o sobrescribir archivos con nuevo contenido. Si el archivo no existe, se crea automáticamente. Si existe, su contenido se reemplaza completamente:
const fs = require('fs');
const datosUsuario = {
nombre: 'Ana García',
email: 'ana@ejemplo.com',
fechaRegistro: new Date().toISOString()
};
// Convertir objeto a JSON y escribir al archivo
const contenidoJSON = JSON.stringify(datosUsuario, null, 2);
fs.writeFile('usuario.json', contenidoJSON, 'utf8', (error) => {
if (error) {
console.error('Error al escribir archivo:', error.message);
return;
}
console.log('Archivo guardado exitosamente');
});
Puedes escribir diferentes tipos de contenido especificando la codificación apropiada:
// Escribir texto plano
const mensaje = 'Bienvenido a nuestra aplicación\nFecha: ' + new Date();
fs.writeFile('bienvenida.txt', mensaje, 'utf8', (error) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('Mensaje de bienvenida creado');
});
// Escribir datos binarios (Buffer)
const buffer = Buffer.from('Datos binarios de ejemplo', 'utf8');
fs.writeFile('datos.bin', buffer, (error) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('Archivo binario creado');
});
Combinando lectura y escritura
Un patrón común es leer un archivo, procesarlo y escribir el resultado. Este ejemplo muestra cómo procesar un archivo de log:
const fs = require('fs');
function procesarLog(archivoEntrada, archivoSalida) {
// Leer el archivo de log
fs.readFile(archivoEntrada, 'utf8', (error, contenido) => {
if (error) {
console.error('Error al leer log:', error.message);
return;
}
// Procesar las líneas del log
const lineas = contenido.split('\n');
const errores = lineas.filter(linea => linea.includes('ERROR'));
// Crear reporte de errores
const reporte = {
fechaProceso: new Date().toISOString(),
totalLineas: lineas.length,
totalErrores: errores.length,
errores: errores
};
// Escribir el reporte procesado
fs.writeFile(archivoSalida, JSON.stringify(reporte, null, 2), 'utf8', (errorEscritura) => {
if (errorEscritura) {
console.error('Error al escribir reporte:', errorEscritura.message);
return;
}
console.log(`Reporte generado: ${errores.length} errores encontrados`);
});
});
}
procesarLog('aplicacion.log', 'reporte-errores.json');
Opciones avanzadas de escritura
El método writeFile
acepta un objeto de opciones como tercer parámetro, permitiendo mayor control sobre la operación:
const fs = require('fs');
const opciones = {
encoding: 'utf8',
mode: 0o644, // Permisos del archivo (lectura/escritura para propietario)
flag: 'w' // 'w' = escribir (por defecto), 'a' = añadir
};
fs.writeFile('configuracion.txt', 'debug=true\nport=3000', opciones, (error) => {
if (error) {
console.error('Error:', error.message);
return;
}
console.log('Configuración guardada con permisos específicos');
});
Para añadir contenido sin sobrescribir el archivo existente, utiliza la flag 'a'
:
const fs = require('fs');
function registrarEvento(mensaje) {
const timestamp = new Date().toISOString();
const entrada = `[${timestamp}] ${mensaje}\n`;
const opciones = {
encoding: 'utf8',
flag: 'a' // Añadir al final del archivo
};
fs.writeFile('eventos.log', entrada, opciones, (error) => {
if (error) {
console.error('Error al registrar evento:', error.message);
return;
}
console.log('Evento registrado en el log');
});
}
registrarEvento('Usuario inició sesión');
registrarEvento('Archivo procesado correctamente');
Operaciones síncronas vs asíncronas
Node.js ofrece dos enfoques fundamentales para trabajar con el sistema de archivos: operaciones asíncronas y operaciones síncronas. Esta distinción es crucial para entender cómo Node.js maneja las operaciones de entrada/salida y cómo afecta al rendimiento de tu aplicación.
Las operaciones asíncronas permiten que tu aplicación continúe ejecutando otras tareas mientras espera que se complete la operación del archivo. Por el contrario, las operaciones síncronas bloquean la ejecución hasta que la operación termine completamente.
Métodos síncronos del módulo fs
Los métodos síncronos terminan con el sufijo Sync
y devuelven directamente el resultado o lanzan una excepción si ocurre un error. No utilizan callbacks ni promesas:
const fs = require('fs');
try {
// Lectura síncrona - bloquea hasta completarse
const contenido = fs.readFileSync('datos.txt', 'utf8');
console.log('Archivo leído:', contenido);
// Escritura síncrona - bloquea hasta completarse
fs.writeFileSync('copia.txt', contenido, 'utf8');
console.log('Archivo copiado exitosamente');
} catch (error) {
console.error('Error en operación síncrona:', error.message);
}
El manejo de errores en operaciones síncronas se realiza mediante bloques try-catch, ya que los errores se lanzan como excepciones:
const fs = require('fs');
function leerArchivoSeguro(ruta) {
try {
const datos = fs.readFileSync(ruta, 'utf8');
return { exito: true, datos };
} catch (error) {
return {
exito: false,
error: error.message,
codigo: error.code
};
}
}
const resultado = leerArchivoSeguro('archivo-inexistente.txt');
if (resultado.exito) {
console.log('Contenido:', resultado.datos);
} else {
console.log('Error:', resultado.error);
}
Comparación práctica de rendimiento
Para entender el impacto en el rendimiento, observa cómo se comportan ambos enfoques al procesar múltiples archivos:
const fs = require('fs');
// Enfoque síncrono - bloquea en cada operación
function procesarArchivosSincrono() {
console.log('Iniciando procesamiento síncrono...');
const inicio = Date.now();
try {
const archivo1 = fs.readFileSync('archivo1.txt', 'utf8');
console.log('Archivo 1 procesado');
const archivo2 = fs.readFileSync('archivo2.txt', 'utf8');
console.log('Archivo 2 procesado');
const archivo3 = fs.readFileSync('archivo3.txt', 'utf8');
console.log('Archivo 3 procesado');
const tiempoTotal = Date.now() - inicio;
console.log(`Procesamiento síncrono completado en ${tiempoTotal}ms`);
} catch (error) {
console.error('Error:', error.message);
}
}
// Enfoque asíncrono - no bloquea
function procesarArchivosAsincrono() {
console.log('Iniciando procesamiento asíncrono...');
const inicio = Date.now();
let archivosCompletados = 0;
const archivoCompletado = () => {
archivosCompletados++;
if (archivosCompletados === 3) {
const tiempoTotal = Date.now() - inicio;
console.log(`Procesamiento asíncrono completado en ${tiempoTotal}ms`);
}
};
fs.readFile('archivo1.txt', 'utf8', (error, datos) => {
if (error) {
console.error('Error archivo 1:', error.message);
return;
}
console.log('Archivo 1 procesado');
archivoCompletado();
});
fs.readFile('archivo2.txt', 'utf8', (error, datos) => {
if (error) {
console.error('Error archivo 2:', error.message);
return;
}
console.log('Archivo 2 procesado');
archivoCompletado();
});
fs.readFile('archivo3.txt', 'utf8', (error, datos) => {
if (error) {
console.error('Error archivo 3:', error.message);
return;
}
console.log('Archivo 3 procesado');
archivoCompletado();
});
}
Cuándo usar cada enfoque
Las operaciones síncronas son apropiadas en situaciones específicas donde el bloqueo no afecta la experiencia del usuario:
const fs = require('fs');
// Carga de configuración al inicio de la aplicación
function cargarConfiguracion() {
try {
const config = fs.readFileSync('./config.json', 'utf8');
return JSON.parse(config);
} catch (error) {
console.error('Error cargando configuración:', error.message);
process.exit(1); // Terminar aplicación si no hay configuración
}
}
// Scripts de inicialización o migración
function inicializarDirectorios() {
const directorios = ['uploads', 'logs', 'temp'];
directorios.forEach(dir => {
try {
fs.mkdirSync(dir, { recursive: true });
console.log(`Directorio ${dir} creado`);
} catch (error) {
if (error.code !== 'EEXIST') {
throw error;
}
}
});
}
// Ejecutar al inicio de la aplicación
const configuracion = cargarConfiguracion();
inicializarDirectorios();
console.log('Aplicación inicializada correctamente');
Las operaciones asíncronas son la elección correcta para la mayoría de casos, especialmente cuando manejas solicitudes de usuarios:
const fs = require('fs');
// Simulación de un endpoint que procesa archivos
function procesarSolicitudUsuario(nombreArchivo, callback) {
console.log(`Procesando solicitud para: ${nombreArchivo}`);
// Operación asíncrona - no bloquea otras solicitudes
fs.readFile(nombreArchivo, 'utf8', (error, contenido) => {
if (error) {
callback({ error: 'Archivo no encontrado' });
return;
}
// Simular procesamiento adicional
const resultado = {
archivo: nombreArchivo,
lineas: contenido.split('\n').length,
caracteres: contenido.length,
procesadoEn: new Date().toISOString()
};
callback({ exito: true, datos: resultado });
});
}
// Simular múltiples solicitudes concurrentes
procesarSolicitudUsuario('documento1.txt', (resultado) => {
console.log('Solicitud 1 completada:', resultado);
});
procesarSolicitudUsuario('documento2.txt', (resultado) => {
console.log('Solicitud 2 completada:', resultado);
});
console.log('Otras operaciones pueden ejecutarse mientras se procesan los archivos');
Versiones con promesas
Node.js también proporciona versiones basadas en promesas del módulo fs, que ofrecen una sintaxis más moderna y facilitan el manejo de operaciones asíncronas:
const fs = require('fs').promises;
// Usando promesas con async/await
async function procesarArchivoModerno() {
try {
const contenido = await fs.readFile('datos.txt', 'utf8');
console.log('Archivo leído con promesas');
const contenidoModificado = contenido.toUpperCase();
await fs.writeFile('datos-mayusculas.txt', contenidoModificado, 'utf8');
console.log('Archivo procesado y guardado');
} catch (error) {
console.error('Error en procesamiento:', error.message);
}
}
// Procesamiento paralelo con Promise.all
async function procesarMultiplesArchivos() {
try {
const promesas = [
fs.readFile('archivo1.txt', 'utf8'),
fs.readFile('archivo2.txt', 'utf8'),
fs.readFile('archivo3.txt', 'utf8')
];
const resultados = await Promise.all(promesas);
console.log(`${resultados.length} archivos procesados en paralelo`);
// Combinar contenidos
const contenidoCombinado = resultados.join('\n---\n');
await fs.writeFile('archivos-combinados.txt', contenidoCombinado, 'utf8');
} catch (error) {
console.error('Error en procesamiento paralelo:', error.message);
}
}
procesarArchivoModerno();
procesarMultiplesArchivos();
Consideraciones de rendimiento y mejores prácticas
El event loop de Node.js es fundamental para entender por qué las operaciones asíncronas son preferibles. Las operaciones síncronas bloquean este bucle, impidiendo que se procesen otras tareas:
const fs = require('fs');
console.log('Inicio del programa');
// Esta operación síncrona bloquea todo lo demás
console.time('Operación síncrona');
try {
const datos = fs.readFileSync('archivo-grande.txt', 'utf8');
console.log('Archivo síncrono procesado');
} catch (error) {
console.error('Error síncrono:', error.message);
}
console.timeEnd('Operación síncrona');
// Esta operación asíncrona permite que continúe la ejecución
console.time('Operación asíncrona');
fs.readFile('archivo-grande.txt', 'utf8', (error, datos) => {
if (error) {
console.error('Error asíncrono:', error.message);
return;
}
console.log('Archivo asíncrono procesado');
console.timeEnd('Operación asíncrona');
});
console.log('Esta línea se ejecuta inmediatamente (operación asíncrona)');
// Simular otras tareas que pueden ejecutarse
setInterval(() => {
console.log('Tarea periódica ejecutándose...');
}, 1000);
La regla general es usar operaciones asíncronas para todas las operaciones de E/S en aplicaciones de producción, reservando las síncronas únicamente para scripts de inicialización, configuración o herramientas de línea de comandos donde el bloqueo no representa un problema.
Otras lecciones de Node
Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación De Node.js
Introducción Y Entorno
Fundamentos Del Entorno Node.js
Introducción Y Entorno
Estructura De Proyecto Y Package.json
Introducción Y Entorno
Introducción A Node
Introducción Y Entorno
Gestor De Versiones Nvm
Introducción Y Entorno
Repl De Nodejs
Introducción Y Entorno
Ejercicios de programación de Node
Evalúa tus conocimientos de esta lección Módulo fs con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.