Lee archivos de forma eficiente en Node.js con el módulo fs
Usa readFile() de node:fs/promises cuando necesites todo el contenido
de un archivo pequeño. Para un archivo grande, procesa un stream fragmento a fragmento. Los ejemplos
siguientes leen la misma entrada como texto, cuentan sus bytes e inspeccionan sus primeros diez bytes
sin modificar el archivo.
Prepara un archivo de ejemplo
Estos ejemplos se probaron con Node.js v26.8.2 en macOS. La preparación usa Bash; no necesitas instalar
paquetes. Guarda los ejemplos de JavaScript con los nombres de archivo .mjs
indicados para que Node.js los cargue como módulos ES,
incluso dentro de un proyecto CommonJS.
Ejecuta esto en el directorio donde quieras crear la demostración:
mkdir node-read-demo &&
cd node-read-demo &&
printf 'Hello, 🌍!\n' > example.txt
La preparación rechaza un directorio node-read-demo existente. Si falla
mkdir o cd, la cadena
&& se detiene antes de escribir example.txt.
Si se produce un fallo, detente y elige una ubicación nueva. Tras completarse correctamente,
permanece en node-read-demo y guarda allí cada script. Los scripts solo muestran
resultados en la terminal; volver a ejecutarlos no modifica la entrada.
Lee un archivo UTF-8 pequeño
Guarda esto como read-text.mjs:
import { readFile } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const text = await readFile(path, 'utf8')
process.stdout.write(text)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
Ejecútalo desde el directorio de la demostración:
node read-text.mjs example.txt
La salida es Hello, 🌍! seguida de un salto de línea, exactamente como está
guardada en el archivo. El argumento opcional tiene como valor predeterminado
example.txt. Las rutas de entrada relativas se resuelven desde el directorio de
trabajo actual de la terminal, no desde el directorio del script.
El argumento 'utf8' hace que
readFile() devuelva una cadena.
Omítelo cuando necesites un Buffer que contenga los bytes originales, por
ejemplo, de una imagen o un archivo contenedor. La decodificación UTF-8 es para texto, no para datos
binarios arbitrarios.
Procesa un archivo grande sin acumular su contenido
Aunque readFile() es asíncrono, carga todo el resultado en memoria. Úsalo cuando
esto se ajuste al presupuesto de memoria de tu aplicación, incluidas las lecturas simultáneas.
Promise.all() sobre una lista de archivos sin límite puede retener muchos resultados
completos a la vez.
Un stream te permite consumir y descartar fragmentos. Guarda esto como
count-bytes.mjs para contar los bytes realmente leídos:
import { createReadStream } from 'node:fs'
async function main() {
const path = process.argv[2] ?? 'example.txt'
let total = 0
for await (const chunk of createReadStream(path)) {
total += chunk.length
}
console.log(`Read ${total} bytes.`)
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node count-bytes.mjs example.txt
Esto muestra Read 13 bytes.. El globo terráqueo ocupa cuatro bytes UTF-8, por lo que
contar caracteres daría un resultado distinto. No se establece una codificación de caracteres en el
stream: cada fragmento es un Buffer de bytes sin procesar. Un archivo vacío
muestra Read 0 bytes..
El bucle for await...of consume el stream y propaga
los fallos de lectura a catch. De forma predeterminada, el stream del archivo
cierra su descriptor al terminar o producirse un error. Este ejemplo mantiene un contador en lugar
de todos los fragmentos; añadir cada fragmento a un array o una cadena haría perder esa ventaja de
memoria.
Para adaptar el bucle al procesamiento secuencial, realiza el trabajo dentro de él y usa
await para esperar a que termine el trabajo asíncrono antes de continuar.
Un fragmento no es necesariamente una línea completa, un objeto JSON o un registro CSV. Usa un
analizador que gestione los límites si tu tarea necesita esos registros. Si solo necesitas el tamaño
que se informa de un archivo, stat() evita por completo leer su contenido.
Lee solo los primeros diez bytes
Para inspeccionar una cabecera binaria, abre un FileHandle y lee un prefijo de
longitud limitada. Guarda esto como read-prefix.mjs:
import { open } from 'node:fs/promises'
async function main() {
const path = process.argv[2] ?? 'example.txt'
const handle = await open(path, 'r')
try {
const buffer = Buffer.alloc(10)
let total = 0
while (total < buffer.length) {
const { bytesRead } = await handle.read(buffer, total, buffer.length - total, total)
if (bytesRead === 0) break
total += bytesRead
}
console.log(`Read ${total} bytes: ${buffer.subarray(0, total).toString('hex')}`)
} finally {
await handle.close()
}
}
main().catch((error) => {
console.error(`Read failed: ${error.code ?? 'UNKNOWN'}`)
process.exitCode = 1
})
node read-prefix.mjs example.txt
Esto muestra Read 10 bytes: 48656c6c6f2c20f09f8c. El último argumento de
handle.read() es la posición en el archivo;
aquí avanza desde cero. Una lectura puede devolver menos bytes de los solicitados, por lo que el
bucle continúa hasta completar diez bytes o alcanzar el final del archivo (EOF,
bytesRead === 0). finally cierra el identificador del archivo
incluso si falla la lectura.
El subarray() limita la salida a los bytes
realmente leídos y excluye el espacio sin utilizar cuando el archivo es corto o está vacío.
La salida hexadecimal también evita decodificar un carácter UTF-8 incompleto: este prefijo de diez
bytes termina en medio del globo terráqueo.
Diagnostica una lectura fallida
Prueba con un nombre de archivo que no exista:
node read-text.mjs missing.txt
Esto muestra Read failed: ENOENT en la salida de error estándar y termina con el estado
1. Los tres scripts informan de los fallos de lectura de esta manera.
Establecer process.exitCode indica el fallo y permite
que termine de escribirse la salida pendiente.
Para ENOENT, comprueba la ruta de entrada y el directorio de trabajo.
EACCES indica un problema de permisos; comprueba que el proceso pueda
recorrer los directorios superiores y leer el archivo. Intenta leerlo directamente en lugar de
comprobar primero con access(): Node.js documenta la
condición de carrera entre la comprobación y la apertura.
Adapta la API al código existente
La forma de fs.readFile() con callback
sigue siendo compatible. En código basado en callbacks, inspecciona el primer argumento,
error, antes de usar los datos; no necesitas convertir el código circundante
a promesas solo para realizar una lectura.
fs.readFileSync() bloquea la ejecución de JavaScript
hasta que termina. Puede ser adecuado para un script corto de línea de comandos o para cargar la
configuración al iniciar. Evita las lecturas bloqueantes en los manejadores de solicitudes que deban
atender a otros clientes mientras haya operaciones de E/S de disco pendientes.
