Leer archivos con Node.js

L

Una de las cosas más comunes que querrá hacer con casi cualquier lenguaje de programación es abrir y leer un archivo. Con la mayoría de los lenguajes, esto es bastante simple, pero para los veteranos de JavaScript puede parecer un poco extraño. Durante muchos años, JavaScript solo estuvo disponible en el navegador, por lo que los desarrolladores de aplicaciones para el usuario solo pueden estar familiarizados con el API FileReader o similar.

Node.js, como probablemente sepa, es muy diferente a su JavaScript típico en el navegador. Tiene su propio conjunto de bibliotecas destinadas a manejar el sistema operativo y las tareas del sistema de archivos, como abrir y leer archivos. En este artículo, le mostraré cómo usar Node.js para leer archivos. Específicamente, usaremos el fs módulo para hacer precisamente eso.

Hay dos formas de abrir y leer un archivo usando el fs módulo:

  • Cargar todo el contenido a la vez (almacenamiento en búfer)
  • Cargar contenido de forma incremental (streaming)

Cada uno de estos métodos se explicará en las dos secciones siguientes.

Almacenamiento en búfer de contenido con fs.readFile

Esta es la forma más común de leer un archivo con Node.js, especialmente para principiantes, debido a su simplicidad y conveniencia. Aunque, como se dará cuenta en la siguiente sección, no es necesariamente el mejor o el más eficiente.

Aquí hay un ejemplo rápido usando fs.readFile:

var fs = require('fs');

fs.readFile('my-file.txt', 'utf8', function(err, data) {
    if (err) throw err;
    console.log(data);
});

los data El argumento de la devolución de llamada contiene el contenido completo del archivo representado como una cadena en utf8 formato. Si omite el utf8 argumento completamente, entonces el método simplemente devolverá el contenido sin procesar en un Buffer objeto. La eliminación de la utf8 argumento en el código anterior (y asumiendo que my-file.txt contiene la cadena “¡Hola!”), obtendríamos esta salida:

$ node read-file.js
<Buffer 48 65 79 20 74 68 65 72 65 21>

Puede que hayas notado que fs.readFile devuelve el contenido en una devolución de llamada, lo que significa que este método se ejecuta de forma asincrónica. Esto debe usarse siempre que sea posible para evitar bloquear el hilo de ejecución principal, pero a veces tiene que hacer las cosas de forma sincrónica, en cuyo caso Node le proporciona un readFileSync método.

Este método funciona exactamente de la misma manera, excepto que el contenido del archivo se devuelve directamente desde la llamada a la función y el hilo de ejecución se bloquea mientras carga el archivo. Normalmente uso esto en las secciones de inicio de mis programas (como cuando estamos cargando archivos de configuración) o en aplicaciones de línea de comandos donde bloquear el hilo principal no es un gran problema.

A continuación, se explica cómo cargar un archivo sincrónicamente con Node:

var fs = require('fs');

try {
    var data = fs.readFileSync('my-file.txt', 'utf8');
    console.log(data);    
} catch(e) {
    console.log('Error:', e.stack);
}

Observe que con la llamada de bloqueo (sincrónica) tenemos que usar try...catch para manejar cualquier error, a diferencia de la versión sin bloqueo (asincrónica) donde los errores simplemente se nos pasan como argumentos.

Aparte de la forma en que estos métodos devuelven datos y manejan errores, funcionan de la misma manera.

Transmisión de contenido con fs.createReadStream

La segunda forma de abrir y leer un archivo es abrirlo como Corriente utilizando el fs.createReadStream método. Todos los flujos de node son instancias del EventEmitter objeto, lo que le permite suscribirse a eventos importantes.

Un objeto de flujo legible puede ser útil por muchas razones, algunas de las cuales incluyen:

  • Huella de memoria más pequeña. Dado que los datos del archivo de destino se cargan en trozos, no se requiere tanta memoria para almacenar los datos en un búfer.
  • Tiempo de respuesta más rápido. Para aplicaciones urgentes, el tiempo entre la solicitud y la respuesta es crítico. Las transmisiones reducen el tiempo de respuesta (especialmente para archivos grandes) ya que no necesitan esperar para cargar todo el archivo antes de devolver los datos.
  • Datos de tuberías. La abstracción de flujo le permite utilizar una interfaz común entre productores y consumidores de datos para pasar esos datos a través de conductos. Esto es muy similar al Tubería Unix concepto.

Aunque realmente no es muy difícil usar las transmisiones, pueden ser un poco intimidantes y no son tan intuitivas como las fs.readFile método. Aquí está el ‘hola mundo’ de la transmisión de archivos:

var fs = require('fs');

var data="";

var readStream = fs.createReadStream('my-file.txt', 'utf8');

readStream.on('data', function(chunk) {
    data += chunk;
}).on('end', function() {
    console.log(data);
});

Este código hace exactamente lo que hace el código de la primera sección, excepto que tenemos que “recopilar” fragmentos de datos antes de imprimirlos en la consola. Si su archivo es bastante pequeño, probablemente solo reciba un solo fragmento, pero para archivos más grandes, como audio y video, tendrá que recopilar varios fragmentos. Este es el caso en el que comenzará a notar el valor real de la transmisión de archivos.

Tenga en cuenta que el ejemplo que mostré anteriormente frustra en su mayoría el propósito de usar un flujo, ya que terminamos recopilando los datos en un búfer (variable) de todos modos, pero al menos le da una idea de cómo funcionan. Un mejor ejemplo que muestra las fortalezas de los flujos de archivos se puede ver aquí, en una ruta Express que maneja una solicitud de archivo:

var fs = require('fs');
var path = require('path');
var http = require('http');

var staticBasePath="./static";

var staticServe = function(req, res) {
    var fileLoc = path.resolve(staticBasePath);
    fileLoc = path.join(fileLoc, req.url);
    
        var stream = fs.createReadStream(fileLoc);

        stream.on('error', function(error) {
            res.writeHead(404, 'Not Found');
            res.end();
        });

        stream.pipe(res);
};

var httpServer = http.createServer(staticServe);
httpServer.listen(8080);

Todo lo que hacemos aquí es abrir el archivo con fs.createReadStream y canalizarlo al objeto de respuesta, res. Incluso podemos suscribirnos a error eventos y manejarlos a medida que ocurren. Es un método mucho mejor para manejar archivos una vez que aprenda a usarlo correctamente. Para obtener un ejemplo y una explicación más completos del código anterior, consulte este artículo sobre la creación de servidores de archivos estáticos con Node.

Conclusión

En este artículo, debería haber aprendido los conceptos básicos de la lectura de archivos, así como algunos métodos de carga avanzados con objetos Stream. Saber cuándo usarlos es la clave y se debe considerar cuidadosamente para aplicaciones con restricciones de memoria o de tiempo.

¿Cuál es su método preferido para manejar archivos? ¿Cómo ha utilizado Streams en el pasado? ¡Háznoslo saber en los comentarios!

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad