Flujos de Node.js: FileStream, Pipes y Eventos
โก Resumen inteligente
Node.js utiliza flujos para leer y escribir datos en pequeรฑos fragmentos en lugar de cargar archivos completos en la memoria, y esta pรกgina explica la creaciรณn de flujos de archivos, el encadenamiento de tuberรญas, el manejo de la contrapresiรณn y el modelo de eventos que mantiene la eficiencia en la transferencia de datos a gran escala.

Tipos de flujos en Node.js
Cada flujo que expone Node.js se clasifica en uno de cuatro tipos bรกsicos, y conocer el tipo que devuelve una API determinada explica cรณmo se comporta dicho flujo antes de escribir cualquier cรณdigo. El mรณdulo de flujo que viene con Node.js define estas categorรญas, y las API de archivos, redes y compresiรณn que se utilizan en este artรญculo se basan en ellas.
| Tipo de corriente | Direcciรณn de datos | API de ejemplo | Caso de uso comรบn |
|---|---|---|---|
| Readable | Solo fuente (los datos fluyen hacia afuera) | fs.createReadStream() | Leer un archivo, recibir un cuerpo de solicitud HTTP |
| Writable | Solo destino (flujo de datos entrante) | fs.createWriteStream() | Escribir un archivo, enviar una respuesta HTTP |
| Duplex | Ambas direcciones, independientemente | net.Socket (socket TCP) | Comunicaciรณn de red bidireccional |
| Transformaciรณn | En ambas direcciones, los datos se modifican durante la transmisiรณn. | zlib.createGzip() | Comprimir o codificar datos en la fase intermedia del proceso. |
Un flujo legible y un flujo escribible son los dos bloques de construcciรณn utilizados a lo largo de este artรญculo; ambos tambiรฉn impulsan un servidor web HTTP En Node.js, un flujo dรบplex combina ambos roles en un solo objeto, mientras que un flujo de transformaciรณn es un flujo dรบplex cuya salida modifica su entrada, como la compresiรณn gzip aplicada a mitad del flujo.
Flujo de archivos en Node.js
Una vez definidas las cuatro categorรญas de flujo, el resto de este artรญculo describe cรณmo se realiza una transferencia de archivos utilizando las API de flujo legible y escribible.
Node hace un uso extensivo de transmisiones como mecanismo de transferencia de datos.
Por ejemplo, cuando envรญa algo a la consola usando la funciรณn console.log, en realidad estรก usando una secuencia para enviar los datos a la consola.
Node.js tambiรฉn tiene la capacidad de transmitir datos desde archivos para que puedan leerse y escribirse adecuadamente. Ahora veremos un ejemplo de cรณmo podemos usar transmisiones para leer y escribir desde archivos. Necesitamos seguir los pasos que se mencionan a continuaciรณn para este ejemplo.
Paso 1) Cree un archivo llamado data.txt que tenga los siguientes datos. Supongamos que este archivo estรก almacenado en la unidad D de nuestra mรกquina local.
Tutorial sobre Node.js
Introducciรณn
Eventos
Conectividad de datos
Usando jazmรญn
Paso 2) Escriba el cรณdigo relevante que utilizarรก secuencias para leer datos del archivo.
var fs = require("fs"); var stream; stream = fs.createReadStream("D://data.txt"); stream.on("data", function(data) { var chunk = data.toString(); console.log(chunk); });
Code Explicaciรณn:-
- Primero debemos incluir los mรณdulos 'fs' que contienen toda la funcionalidad necesaria para crear transmisiones.
- A continuaciรณn, creamos una secuencia legible utilizando el mรฉtodo โ createReadStream. Como entrada, damos la ubicaciรณn de nuestro archivo data.txt.
- La funciรณn steam.on es un controlador de eventos y en รฉl, especificamos el primer parรกmetro como "datos". Esto significa que cada vez que ingresan datos en la secuencia del archivo, se ejecuta una funciรณn de devoluciรณn de llamada. En nuestro caso, estamos definiendo una funciรณn de devoluciรณn de llamada que realizarรก 2 pasos bรกsicos. La primera es convertir los datos leรญdos del archivo como una cadena. El segundo serรญa enviar la cadena convertida como salida a la consola.
- Tomamos cada fragmento de datos que se lee del flujo de datos y lo convertimos en una cadena.
- Finalmente, enviamos la salida de cada fragmento de cadena convertido a la consola.
Salida:
- Si el cรณdigo se ejecuta correctamente, verรก el resultado anterior en la consola. Esta salida serรก la misma que la del archivo data.txt.
Escribir en un archivo
De la misma manera que creamos una secuencia de lectura, tambiรฉn podemos crear una secuencia de escritura para escribir datos en un archivo. Primero creemos un archivo vacรญo sin contenido llamado data.txt. Supongamos que este archivo estรก ubicado en la unidad D de nuestra computadora.
El siguiente cรณdigo muestra cรณmo podemos escribir datos en el archivo.
var fs = require("fs"); var stream; stream = fs.createWriteStream("D://data.txt"); stream.write("Tutorial on Node.js") stream.write("Introduction") stream.write("Events") stream.write("Generators") stream.write("Data Connectivity") stream.write("Using Jasmine")
Code Explicaciรณn:-
- Estamos creando una secuencia de escritura utilizando el mรฉtodo โ createWriteStream. Como entrada, damos la ubicaciรณn de nuestro archivo data.txt.
- A continuaciรณn usamos stream.write un mรฉtodo para escribir las diferentes lรญneas de texto en nuestro archivo de texto. La transmisiรณn se encargarรก de escribir estos datos en el archivo data.txt.
Si abre el archivo data.txt, ahora verรก los siguientes datos en el archivo
Tutorial sobre Node.js
Introducciรณn
Eventos
Generators
Conectividad de datos
El uso de Jazmรญn
Tuberรญas en Node.js
Leer y escribir flujos de forma independiente es รบtil, pero Node.js tambiรฉn permite que un flujo de lectura se conecte directamente a un flujo de escritura, transfiriendo datos sin necesidad de cรณdigo de almacenamiento en bรบfer manual.
Dentro de las aplicaciones de Node, las transmisiones se pueden canalizar entre sรญ utilizando el mรฉtodo pipe(), que toma dos argumentos:
- Una secuencia de escritura requerida que actรบa como destino de los datos y
- Un objeto opcional utilizado para pasar opciones.
Un ejemplo tรญpico de uso de canalizaciones, si desea transferir datos de un archivo a otro.
Entonces, veamos un ejemplo de cรณmo podemos transferir datos de un archivo a otro usando tuberรญas.
Paso 1) Cree un archivo llamado datainput.txt que tenga los siguientes datos. Supongamos que este archivo estรก almacenado en la unidad D de nuestra mรกquina local.
Tutorial sobre Node.js
Introducciรณn
Eventos
Generators
Conectividad de datos
Usando jazmรญn
Paso 2) Cree un archivo vacรญo en blanco llamado dataOutput.txt y colรณquelo en la unidad D de su mรกquina local.
Paso 3) Escriba el siguiente cรณdigo para llevar a cabo la transferencia de datos desde el archivo datainput.txt al archivo dataOutput.txt.
var fs = require("fs"); var readStream = fs.createReadStream("D://datainput.txt"); var writeStream = fs.createWriteStream("D://dataOutput.txt"); readStream.pipe(writeStream);
Code Explicaciรณn:-
- Primero estamos creando una "secuencia de lectura" para nuestro archivo datainput.txt que contiene todos nuestros datos que deben transferirse al nuevo archivo.
- Luego necesitamos crear un "flujo de escritura" en nuestro archivo dataOutput.txt, que es nuestro archivo vacรญo y es el destino para la transferencia de datos desde el archivo datainput.txt.
- Luego usamos el comando pipe para transferir los datos del flujo de lectura al flujo de escritura. El comando pipe tomarรก todos los datos que ingresan al flujo de lectura y los enviarรก al flujo de escritura.
Si ahora abre el archivo dataOutput.txt, verรก todos los datos que estaban presentes en el archivo datainput.txt.
Eventos de transmisiรณn y contrapresiรณn en Node.js
Llamar a pipe(), como se muestra arriba, oculta dos mecanismos importantes a gran escala: los eventos que emite cada flujo y el sistema de contrapresiรณn que impide que una fuente rรกpida sature un destino lento.
- datos: emitido por un flujo legible cada vez que hay un nuevo fragmento disponible.
- fin: Se emite una vez que una secuencia legible ha entregado su รบltimo fragmento.
- terminar: emitido por un flujo escribible una vez que se han vaciado todos los datos en cola.
- error: Emitido por cualquiera de los tipos de flujo en caso de fallo; siempre manรฉjelo o el proceso fallarรก.
El ejemplo que se muestra a continuaciรณn escucha los cuatro eventos mientras gestiona manualmente la contrapresiรณn, el mismo comportamiento que pipe() proporciona automรกticamente.
var fs = require("fs"); var readStream = fs.createReadStream("D://data.txt"); var writeStream = fs.createWriteStream("D://dataOutput.txt"); readStream.on("data", function(chunk) { var ok = writeStream.write(chunk); if (!ok) { readStream.pause(); } }); writeStream.on("drain", function() { readStream.resume(); }); readStream.on("end", function() { writeStream.end(); }); writeStream.on("finish", function() { console.log("Write completed."); }); readStream.on("error", function(err) { console.log(err); });
La contrapresiรณn se produce cuando un flujo de lectura genera datos mรกs rรกpido de lo que un flujo de escritura puede consumirlos. El mรฉtodo `write()` devuelve `false` una vez que su bรบfer se llena, por lo que el cรณdigo anterior pausa el origen hasta que el destino emite un evento de drenaje, lo que indica que es seguro reanudarlo. El mรฉtodo `pipe()` realiza este ciclo automรกticamente.
โ Consejo: En producciรณn, es preferible usar la funciรณn pipeline() del mรณdulo stream en lugar de una cadena pipe() manual, ya que reenvรญa los errores de cada flujo y cierra automรกticamente los descriptores de archivo.
Eventos en Node.js
Los flujos son un ejemplo del diseรฑo orientado a eventos de Node.js. El resto de este artรญculo analiza cรณmo funcionan los eventos en general, utilizando la misma clase EventEmitter en la que se basan internamente los flujos.
Los eventos son uno de los conceptos clave en Node.js y, a veces, se hace referencia a Node.js como un marco basado en eventos.
Bรกsicamente, un evento es algo que sucede. Por ejemplo, si se establece una conexiรณn con una base de datos, se activa el evento de conexiรณn de la base de datos. La programaciรณn impulsada por eventos consiste en crear funciones que se activarรกn cuando se activen eventos especรญficos.
Veamos un ejemplo bรกsico de definiciรณn de un evento en Node.js.
Vamos a crear un evento llamado "datos_recibidos". Cuando se activa este evento, el texto "datos recibidos" se enviarรก a la consola.
var events = require('events'); var eventEmitter = new events.EventEmitter(); eventEmitter.on('data_received', function() { console.log('data received succesfully.'); }); eventEmitter.emit('data_received');
Code Explicaciรณn:-
- Utilice la funciรณn require para incluir el mรณdulo 'eventos'. Con este mรณdulo, podrรกs crear eventos en Node.js.
- Crea un nuevo emisor de eventos. Esto se utiliza para vincular el evento, que en nuestro caso es "datos_recibidos" a una funciรณn de devoluciรณn de llamada que se define en el paso 3.
- Definimos una funciรณn controlada por eventos que dice que si en caso de que se active el evento "data_received", debemos enviar el texto "data_received" a la consola.
- Finalmente, tenemos un disparador manual de nuestro evento usando la funciรณn eventEmiter.emit. Esto activarรก el evento data_received.
Cuando se ejecuta el programa, el texto "datos recibidos" se enviarรก a la consola como se muestra a continuaciรณn.
Eventos de emisiรณn
Ademรกs de emitir y escuchar eventos, Node.js ofrece mรฉtodos adicionales para controlar cรณmo se registran e inspeccionan los oyentes.
Al definir eventos, existen diferentes mรฉtodos para los eventos que se pueden invocar. Este tema se centra en mirar cada uno de ellos en detalle.
- Controladores de eventos รบnicos
A veces puede que le interese reaccionar ante un evento sรณlo la primera vez que ocurre. En estas situaciones, puede utilizar el mรฉtodo once().
Veamos cรณmo podemos utilizar el mรฉtodo once para controladores de eventos.
Code Explicaciรณn:-
- Aquรญ estamos usando el mรฉtodo "once" para decir que para el evento "data_received", la funciรณn de devoluciรณn de llamada solo debe ejecutarse una vez.
- Aquรญ estamos activando manualmente el evento "datos_recibidos".
- Cuando el evento 'data_received' se active nuevamente, esta vez no sucederรก nada. Esto se debe al primer paso en el que dijimos que el evento solo podรญa activarse una vez.
Si el cรณdigo se ejecuta correctamente, el resultado en el registro serรก "datos_recibidos correctamente". Este mensaje solo aparecerรก una vez en la consola.
- Inspeccionar oyentes de eventos
En cualquier momento de su vida, un emisor de eventos puede tener cero o mรกs oyentes asociados. Los oyentes de cada tipo de evento se pueden inspeccionar de varias maneras.
Si solo estรก interesado en determinar el nรบmero de oyentes conectados, no busque mรกs que el mรฉtodo EventEmitter.listenerCount().
(Nota: Los oyentes son importantes porque el programa principal debe saber si se estรกn agregando oyentes sobre la marcha a un evento; de lo contrario, el programa funcionarรก mal porque se llamarรก a oyentes adicionales).
Code Explicaciรณn:-
- Estamos definiendo un tipo eventEmitter que es necesario para utilizar los mรฉtodos relacionados con eventos.
- Luego estamos definiendo un objeto llamado emisor que se utilizarรก para definir nuestros controladores de eventos.
- Estamos creando 2 controladores de eventos que bรกsicamente no hacen nada. En nuestro ejemplo, esto se mantiene simple para mostrar cรณmo funciona el mรฉtodo listenerCount.
- Ahora, cuando invoque el mรฉtodo listenerCount en nuestro evento data_received, enviarรก la cantidad de detectores de eventos adjuntos a este evento en el registro de la consola.
Si el cรณdigo se ejecuta correctamente, el valor de 2 se mostrarรก en el registro de la consola.
- El nuevo evento Listener
Cada vez que se registra un nuevo controlador de eventos, el emisor de eventos emite un evento newListener. Este evento se utiliza para detectar nuevos controladores de eventos. Normalmente utiliza el evento newListener cuando necesita asignar recursos o realizar alguna acciรณn para cada nuevo controlador de eventos.
var events = require('events'); var eventEmitter = events.EventEmitter; var emitter = new eventEmitter(); emitter.on("newListener", function(eventName, listener) { console.log("Added listener for " + eventName + " events"); }); emitter.on('data_received', function() {}); emitter.on('data_received', function() {});
Code Explicaciรณn:-
- Estamos creando un nuevo controlador de eventos para el evento "newListener". Entonces, cada vez que se registra un nuevo controlador de eventos, el texto "Oyente agregado para" + el nombre del evento se mostrarรก en la consola.
- Aquรญ escribimos en la consola el texto โOyente agregado paraโ + el nombre del evento para cada evento registrado.
- Estamos definiendo 2 controladores de eventos para nuestro evento 'data_received'.
Si el cรณdigo anterior se ejecuta correctamente, el siguiente texto se mostrarรก en la consola. Simplemente muestra que el controlador de eventos 'newListener' se activรณ dos veces.
Se agregรณ un oyente para eventos data_received
Se agregรณ un oyente para eventos data_received









