Módulo Stream de Node.js

Java FileMódulos integrados de Node.js


Stream (flujo) es una interfaz abstracta para manejar datos en streaming en Node.js. Puedes imaginar un stream como un flujo de agua; los datos fluyen de un lugar a otro como el agua. Este método de procesamiento es especialmente adecuado para archivos grandes o fuentes de datos continuas, porque no necesita cargar todos los datos en memoria de una sola vez.

¿Por qué se necesita Stream?

  1. Eficiencia de memoria: al procesar archivos grandes, no es necesario cargar todo el archivo en memoria de una vez
  2. Eficiencia de tiempo: se pueden procesar los datos a medida que se reciben, sin necesidad de esperar a que todos los datos estén listos
  3. Componibilidad: múltiples streams se pueden conectar como tuberías para formar una línea de procesamiento de datos.

Los cuatro tipos básicos de Stream

1. Readable Stream (flujo legible)

Un flujo legible es el origen de los datos; se pueden leer datos de él. Por ejemplo, leer contenido de un archivo u obtener datos de una solicitud HTTP.

Ejemplo

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

readableStream.on('data', (chunk) => {
  console.log(`Se recibieron ${chunk.length}bytes de datos`);
});

readableStream.on('end', () => {
  console.log('No hay más datos');
});

2. Writable Stream (flujo de escritura)

Un flujo escribible es el destino de los datos; se pueden escribir datos en él. Por ejemplo, escribir en un archivo o enviar una respuesta HTTP.

Ejemplo

const fs = require('fs');
const writableStream = fs.createWriteStream('output.txt');

writableStream.write('Primera línea de datos\n');
writableStream.write('Segunda línea de datos\n');
writableStream.end('Última línea de datos');

3. Duplex Stream (flujo dúplex)

Un flujo dúplex es tanto legible como escribible, por ejemplo, un socket TCP.

Ejemplo

const { Duplex } = require('stream');

const myDuplex = new Duplex({
  write(chunk, encoding, callback) {
    console.log(chunk.toString());
    callback();
  },
 
  read(size) {
    this.push('Datos leídos del flujo dúplex');
    this.push(null); // Indica que los datos han terminado
  }
});

4. Transform Stream (flujo de transformación)

Un flujo de transformación es un tipo especial de flujo dúplex que puede modificar o transformar los datos durante el proceso de lectura y escritura. Por ejemplo, comprimir o cifrar datos.

Ejemplo

const { Transform } = require('stream');

const upperCaseTr = new Transform({
  transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  }
});

process.stdin.pipe(upperCaseTr).pipe(process.stdout);

Métodos comunes de Stream

Método pipe()

pipe()El método es una de las características más potentes de Stream; puede conectar múltiples streams para formar un conducto.

Ejemplo

const fs = require('fs');

// Crea un flujo de lectura y un flujo de escritura
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');

// Usa pipe para conectar el flujo de lectura y el flujo de escritura
readStream.pipe(writeStream);

writeStream.on('finish', () => {
  console.log('Escritura de datos completada');
});

Escucha de eventos

Stream es una instancia de EventEmitter; puede manejar varias situaciones escuchando eventos:

  • data- Se activa cuando hay datos legibles.
  • end- Se activa cuando no hay más datos por leer.
  • error- Se activa cuando ocurre un error.
  • finish- Se activa cuando todos los datos han sido vaciados al sistema subyacente.

Ejemplos de aplicaciones prácticas

Ejemplo 1: Copia de archivos

Ejemplo

const fs = require('fs');

function copyFile(source, target, cb) {
  // Crea flujos de lectura y escritura
  const rd = fs.createReadStream(source);
  const wr = fs.createWriteStream(target);

  // Maneja errores
  rd.on('error', err => cb(err));
  wr.on('error', err => cb(err));
 
  // Callback de finalización
  wr.on('finish', cb);
 
  // Inicia la copia
  rd.pipe(wr);
}

// Uso
copyFile('source.txt', 'target.txt', err => {
  if (err) console.error(err);
  else console.log('Copia completada');
});

Ejemplo 2: Compresión de archivos en servidor HTTP

Ejemplo

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

http.createServer((req, res) => {
  // Crea un flujo de lectura
  const readStream = fs.createReadStream('./largeFile.txt');
 
  // Establece los encabezados de la respuesta
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Content-Encoding': 'gzip'
  });
 
  // Cadena de tuberías: lectura -> compresión -> respuesta
  readStream.pipe(zlib.createGzip()).pipe(res);
}).listen(3000);

Mejores prácticas de Stream

  1. Manejo de errores: agrega siempre un listener de manejo de errores al stream.
  2. Gestión de memoria: para archivos grandes, usa streams en lugar de leer todo de una vez.
  3. Cadena de tuberías: usa adecuadamente el método pipe() para combinar múltiples streams.
  4. Manejo de contrapresión: cuando la velocidad de escritura no puede seguir el ritmo de la velocidad de lectura, es necesario manejar adecuadamente la contrapresión.

Ejemplo de manejo de errores

Ejemplo

const fs = require('fs');

const readStream = fs.createReadStream('not-exist-file.txt');

readStream.on('error', (err) => {
  console.error('Ocurrió un error:', err.message);
});

Al dominar el módulo Stream de Node.js, puedes manejar eficientemente varias operaciones de E/S, especialmente al procesar archivos grandes o datos en tiempo real; los streams se convertirán en una herramienta poderosa para ti.

Java FileMódulos integrados de Node.js

Otras extensiones