Node.js Stream (flujo)

Stream de Node.js es una interfaz abstracta para manejar datos en flujo, ampliamente utilizada en operaciones de archivos, comunicaciones de red, etc.

Una de las principales ventajas del procesamiento de datos en flujo es que se pueden empezar a procesar los datos durante la transmisión sin tener que esperar a que se carguen todos los datos, lo que permite a Node.js manejar grandes cantidades de datos de manera eficiente sin consumir demasiada memoria.

Stream es una interfaz abstracta; en Node muchos objetos implementan esta interfaz. Por ejemplo, el objeto request de una petición a un servidor http es un Stream, al igual que stdout (salida estándar).

Stream (flujo) de Node.js es una forma de manejar flujos de datos que te permite procesar datos en forma de flujo en lugar de cargarlos todos en la memoria de una vez. Esto es muy útil para manejar grandes volúmenes de datos o lograr una transferencia de datos eficiente.

En Node.js, Stream tiene cuatro tipos de flujo:

  • Readable- Operación de lectura.

  • Writable- Operación de escritura.

  • Duplex- Operación de lectura y escritura.

  • Transform- La operación escribe datos y luego lee el resultado.

Todos los objetos Stream son instancias de EventEmitter. Los eventos comunes son:

  • data- Se dispara cuando hay datos para leer.

  • end- Se dispara cuando no hay más datos para leer.

  • error- Se dispara cuando ocurre un error durante la recepción y escritura.

  • finish- Se dispara cuando todos los datos han sido escritos en el sistema subyacente.

Este tutorial presentará las operaciones de flujo más comunes.


Leer datos de un flujo

Los flujos legibles comunes incluyen flujos de lectura de archivos y flujos de respuesta a solicitudes de red.

Crea un archivo input.txt con el siguiente contenido:

Ejemplo官网地址:www.example.com

Crea un archivo main.js, con el siguiente código:

Ejemplo

var fs = require("fs");
var data = '';

// Crear un flujo legible
var readerStream = fs.createReadStream('input.txt');

// Establecer la codificación a utf8.
readerStream.setEncoding('UTF8');

// Manejar eventos de flujo --> data, end y error
readerStream.on('data', function(chunk) {
   data += chunk;
});

readerStream.on('end',function(){
   console.log(data);
});

readerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("El programa ha terminado de ejecutarse");

El resultado de ejecutar el código anterior es el siguiente:

程序执行完毕
Ejemplo官网地址:www.example.com

Flujo de escritura

Los flujos de escritura se utilizan para escribir datos en un destino; los flujos de escritura comunes incluyen flujos de escritura de archivos y flujos de envío de solicitudes de red.

Crea un archivo main.js, con el siguiente código:

Ejemplo

var fs = require("fs");
var data = 'Dirección web oficial del tutorial de novatos: www.example.com';

// Crear un flujo de escritura, escribir en el archivo output.txt
var writerStream = fs.createWriteStream('output.txt');

// Escribir datos usando codificación utf8
writerStream.write(data,'UTF8');

// Marcar el final del archivo
writerStream.end();

// Manejar eventos de flujo --> finish, error
writerStream.on('finish', function() {
    console.log("Escritura completada.");
});

writerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("El programa ha terminado de ejecutarse");

El programa anterior escribirá los datos de la variable data en el archivo output.txt. El resultado de ejecutar el código es el siguiente:

$ node main.js 
程序执行完毕
写入完成。

Ver el contenido del archivo output.txt:

$ cat output.txt 
Ejemplo官网地址:www.example.com

Flujo dúplex (Duplex)

Un flujo dúplex tiene capacidades de lectura y escritura al mismo tiempo.

Un ejemplo típico de flujo dúplex es un socket TCP.

Ejemplo

const net = require('net');

// Crear un servidor TCP
const server = net.createServer((socket) => {
    console.log('Client connected.');

    // Leer datos del cliente
    socket.on('data', (data) => {
        console.log('Received data:', data.toString());
    });

    // Enviar datos al cliente
    socket.write('Hello, Client!\n');

    // Escuchar el evento de cierre
    socket.on('end', () => {
        console.log('Client disconnected.');
    });
});

server.listen(3000, () => {
    console.log('Server listening on port 3000.');
});

Flujo de transformación (Transform)

Un flujo de transformación es un tipo especial de flujo dúplex que puede modificar o transformar datos. Los flujos de transformación comunes incluyen flujos de compresión y descompresión.

Ejemplo

const zlib = require('zlib');
const fs = require('fs');

// Crear un flujo legible
const readableStream = fs.createReadStream('example.txt');

// Crear un flujo de transformación (compresión)
const gzip = zlib.createGzip();

// Crear un flujo de escritura
const writableStream = fs.createWriteStream('example.txt.gz');

// Pasar el flujo legible al flujo de transformación mediante pipe, y luego al flujo de escritura
readableStream.pipe(gzip).pipe(writableStream);

// Escuchar el evento de finalización
writableStream.on('finish', () => {
    console.log('File compressed successfully.');
});

Flujo de tubería

El pipe proporciona un mecanismo de flujo de salida a flujo de entrada.

Normalmente se utiliza para obtener datos de un flujo y pasarlos a otro flujo.

Como se muestra en la imagen anterior, comparamos los archivos con cubos de agua, donde el agua es el contenido del archivo. Usamos una tubería (pipe) para conectar dos cubos, haciendo que el agua fluya de un cubo a otro, logrando así gradualmente el proceso de copia de archivos grandes.

En el siguiente ejemplo, leemos el contenido de un archivo y lo escribimos en otro archivo.

Establece el contenido del archivo input.txt de la siguiente manera:

Ejemplo官网地址:www.example.com
管道流操作实例

Crea un archivo main.js, con el siguiente código:

Ejemplo

var fs = require("fs");

// Crear un flujo legible
var readerStream = fs.createReadStream('input.txt');

// Crear un flujo de escritura
var writerStream = fs.createWriteStream('output.txt');

// Operación de lectura y escritura mediante pipe
// Leer el contenido del archivo input.txt y escribirlo en el archivo output.txt
readerStream.pipe(writerStream);

console.log("El programa ha terminado de ejecutarse");

El resultado de ejecutar el código es el siguiente:

$ node main.js 
程序执行完毕

Ver el contenido del archivo output.txt:

$ cat output.txt 
Ejemplo官网地址:www.example.com
管道流操作实例

Flujo en cadena

La cadena es un mecanismo que conecta un flujo de salida a otro flujo y crea una cadena de operaciones de flujo múltiples.

El flujo en cadena se usa generalmente para operaciones de pipe.

A continuación, usaremos pipe y cadenas para comprimir y descomprimir archivos.

Crea un archivo compress.js, con el siguiente código:

Ejemplo

var fs = require("fs");
var zlib = require('zlib');

// Comprimir el archivo input.txt a input.txt.gz
fs.createReadStream('input.txt')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('input.txt.gz'));
 
console.log("Compresión de archivo completada.");

El resultado de ejecutar el código es el siguiente:

$ node compress.js 
文件压缩完成。

Después de ejecutar las operaciones anteriores, podemos ver que se ha generado el archivo comprimido input.txt.gz de input.txt en el directorio actual.

A continuación, descomprimamos ese archivo. Crea un archivo decompress.js, con el siguiente código:

Ejemplo

var fs = require("fs");
var zlib = require('zlib');

// Descomprimir el archivo input.txt.gz a input.txt
fs.createReadStream('input.txt.gz')
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('input.txt'));
 
console.log("Descompresión de archivo completada.");

El resultado de ejecutar el código es el siguiente:

$ node decompress.js 
文件解压完成。

Pausar y reanudar

Un flujo legible puede pausar y reanudar la lectura de datos.

Ejemplo

const fs = require('fs');

const readableStream = fs.createReadStream('example.txt', 'utf8');

readableStream.on('data', (chunk) => {
    console.log('Received chunk:', chunk);
    readableStream.pause(); // Pausar lectura
    setTimeout(() => {
        readableStream.resume(); // Reanudar lectura
    }, 1000);
});

Destruir

Se puede destruir el flujo para liberar recursos.

Ejemplo

const fs = require('fs');

const readableStream = fs.createReadStream('example.txt', 'utf8');

readableStream.on('data', (chunk) => {
    console.log('Received chunk:', chunk);
    readableStream.destroy(); // Destruir flujo
});
Otras extensiones