Fermer

avril 29, 2025

Node.js Streams avec TypeScript –

Node.js Streams avec TypeScript –


Node.js est réputé pour sa capacité à gérer efficacement les opérations d’E / S, et au cœur de cette capacité se trouve le concept de ruisseaux. Les flux vous permettent de traiter des données pièce par morceau, plutôt que de tout charger en mémoire à la fois – parfait pour gérer les fichiers volumineux, les demandes de réseau ou les données en temps réel. Lorsque vous associez des flux avec le dactage fort de TypeScript, vous obtenez un combo puissant: les performances rencontrent la sécurité.

Dans ce guide, nous plongerons profondément dans les flux Node.js, explorerons leurs types et parcourons des exemples pratiques en utilisant TypeScript. Que vous soyez un débutant Node.js ou un passionné de dactylographie cherchant à monter de niveau, ce message vous a couvert.

Pourquoi les flux sont importants?

Imaginez ceci: vous êtes chargé de traiter un fichier journal de 50 Go. Le charger entièrement dans la mémoire épuiserait les ressources de votre serveur, conduisant à des accidents ou à des performances lentes. Les flux résolvent cela en vous permettant de gérer les données au fur et à mesure de celles-ci, comme en sirotant une paille au lieu de chouger une cruche de gallon.

Cette efficacité explique pourquoi les flux sont une pierre angulaire de Node.js, alimentant tout, des opérations de fichiers aux serveurs HTTP. TypeScript améliore cela en ajoutant des définitions de type, en attrapant des erreurs au moment de la compilation et en améliorant la lisibilité du code. Plongeons dans les fondamentaux et voyons comment cette synergie fonctionne dans la pratique.

Les quatre types de flux

Offres Node.js Quatre types de flux principauxchacun avec un objectif spécifique:

  1. Streams lisibles: Sources de données que vous pouvez lire (par exemple, fichiers, réponses HTTP).
  2. Streams en écriture: Destinations auxquelles vous pouvez écrire (par exemple, fichiers, requêtes HTTP).
  3. Streams duplex: À la fois lisible et écrit (par exemple, les prises TCP).
  4. Transformer les flux: Un flux duplex spécial qui modifie les données au fur et à mesure de son passage (par exemple, compression).

TypeScript améliore cela en nous permettant de définir des interfaces pour les données qui les traversent. Décomposons-les avec des exemples.

Configuration de votre environnement TypeScript

Avant de plonger dans le code, assurez-vous que Node.js et TypeScript sont installés.

Créer un nouveau projet:

mkdir node-streams-typescript
cd node-streams-typescript
npm init -y
npm install typescript @types/node --save-dev
npx tsc --init

Mettez à jour votre tsconfig.json pour inclure:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"]
}

Créez un dossier SRC et commençons à coder!

Exemple 1: lire un fichier avec un flux lisible

Lisons un morceau de fichier texte par Chunk. Tout d’abord, créez un fichier nommé Data.txt dans le répertoire racine de votre projet avec un exemple de texte (par exemple, «Bonjour, Streams!»).

Maintenant, dans src / readstream.ts:

import { createReadStream } from 'fs';
import { Readable } from 'stream';

const readStream: Readable = createReadStream('data.txt', { encoding: 'utf8' });

readStream
  .on('data', (chunk: string) => {
    console.log('Chunk received:', chunk);
  })
  .on('end', () => {
    console.log('Finished reading the file.');
  })
  .on('error', (err: Error) => {
    console.error('Error:', err.message);
  });

Exécutez-le avec:

npx tsc && node dist/readStream.js

Ici, TypeScript garantit que le morceau adhère à notre interface de morceaux, et le gestionnaire d’événements d’erreur s’attend à un type d’erreur. Ce flux lit data.txt en morceaux (par défaut 64KB pour les fichiers) et les enregistre.

Exemple 2: Rédaction de données avec un flux écrit

Maintenant, écrivons des données sur un nouveau fichier. Dans src / writeStream.ts:

import { createWriteStream } from 'fs';
import { Writable } from 'stream';

const writeStream: Writable = createWriteStream('output.txt', { encoding: 'utf8' });

const data: string[] = ['Line 1\n', 'Line 2\n', 'Line 3\n'];

data.forEach((line: string) => {
  writeStream.write(line);
});

writeStream.end(() => {
  console.log('Finished writing to output.txt');
});

writeStream.on('error', (err: Error) => {
  console.error('Error:', err.message);
});

Compiler et courir:

npx tsc && node dist/writeStream.js

Cela crée une sortie.txt avec trois lignes. TypeScript garantit que la ligne est une chaîne et fournit une assurance automatique pour les méthodes de flux.

Exemple 3: Piping avec un flux de transformation

La tuyauterie est l’endroit où les flux brillent, connectant un flux lisible à un flux écrite. Ajoutons une torsion avec un Transformer Diffusez-vous en majuscules notre texte.

Dans src / transformstream.ts:

import { createReadStream, createWriteStream } from 'fs';
import { Transform, TransformCallback } from 'stream';


class UppercaseTransform extends Transform {
  _transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
    const upperChunk = chunk.toString().toUpperCase();
    this.push(upperChunk);
    callback();
  }
}

const readStream = createReadStream('data.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('output_upper.txt');
const transformStream = new UppercaseTransform();

readStream
  .pipe(transformStream)
  .pipe(writeStream)
  .on('finish', () => {
    console.log('Transform complete! Check output_upper.txt');
  })
  .on('error', (err: Error) => {
    console.error('Error:', err.message);
  });

Exécutez-le:

npx tsc && node dist/transformStream.js

Cela lit data.txt, transforme le texte en majuscule et l’écrit en output_upper.txt.

Le type TransformCallback de TypeScript garantit que notre méthode _TRANSFORM est correctement implémentée.

Exemple 4: Compression de fichiers avec un flux duplex

Attorons un scénario plus avancé: comprimer un fichier à l’aide du module ZLIB, qui fournit un flux duplex. Il est livré avec le package «@ types / nœud», que nous avons installé plus tôt.

Dans src / compressstream.ts:

import { createReadStream, createWriteStream } from 'fs';
import { createGzip } from 'zlib';
import { pipeline } from 'stream';

const source = createReadStream('data.txt');
const destination = createWriteStream('data.txt.gz');
const gzip = createGzip();

pipeline(source, gzip, destination, (err: Error | null) => {
  if (err) {
    console.error('Compression failed:', err.message);
    return;
  }
  console.log('File compressed successfully! Check data.txt.gz');
});

Exécutez-le:

npx tsc && node dist/compressStream.js

Ici, le pipeline assure une bonne gestion des erreurs et un nettoyage. Le flux GZIP compresse Data.txt dans data.txt.gz. L’inférence du type de typeScript maintient notre code propre et sûr.

Exemple 5: Réponses HTTP en streaming

Les flux brillent dans les opérations du réseau. Simulons les données de streaming à partir d’un serveur HTTP à l’aide d’Axios. L’installez:

npm install axios @types/axios

Dans src / httpsstream.ts:

import axios from 'axios';
import { createWriteStream } from 'fs';
import { Writable } from 'stream';

async function streamHttpResponse(url: string, outputFile: string): Promise<void> {
  const response = await axios({
    method: 'get',
    url,
    responseType: 'stream',
  });

  const writeStream: Writable = createWriteStream(outputFile);
  response.data.pipe(writeStream);

  return new Promise((resolve, reject) => {
    writeStream.on('finish', () => {
      console.log(`Downloaded to ${outputFile}`);
      resolve();
    });
    writeStream.on('error', (err: Error) => {
      console.error('Download failed:', err.message);
      reject(err);
    });
  });
}

streamHttpResponse('https://example.com', 'example.html').catch(console.error);

Exécutez-le:

npx tsc && node dist/httpStream.js

Cela diffuse une réponse HTTP (par exemple, une page Web) à Exemple.html. TypeScript garantit que l’URL et les paramètres de fichier de sortie sont des chaînes, et la saisie de promesse ajoute de la clarté.

Nous pouvons également utiliser l’API Fetch intégrée de Node.js (disponible depuis le nœud v18) ou des bibliothèques comme Node-Fetch, qui prennent également en charge les réponses en streaming, bien que les types de flux puissent différer (flux Web par rapport aux flux Node.js).

Exemple:

const response = await fetch('https://example.com');
const writeStream = createWriteStream(outputFile);
response.body.pipe(writeStream);

Exemple 6: Traitement des données en temps réel avec un flux lisible sur mesure

Créons un flux personnalisé et lisible pour simuler des données en temps réel, telles que les lectures de capteurs. Dans SRC / CustomAredable.ts:

import { Readable } from 'stream';

class SensorStream extends Readable {
  private count: number = 0;
  private max: number = 10;

  constructor(options?: any) {
    super(options);
  }

  _read(): void {
    if (this.count < this.max) {
      const data = `Sensor reading ${this.count}: ${Math.random() * 100}\n`;
      this.push(data);
      this.count++;
    } else {
      this.push(null); 
    }
  }
}

const sensor = new SensorStream({ encoding: 'utf8' });

sensor
  .on('data', (chunk: string) => {
    console.log('Received:', chunk.trim());
  })
  .on('end', () => {
    console.log('Sensor stream complete.');
  })
  .on('error', (err: Error) => {
    console.error('Error:', err.message);
  });

Exécutez-le:

npx tsc && node dist/customReadable.js

Cela génère 10 «lectures de capteurs» aléatoires et les diffuse. Le typage de classe de TypeScript garantit que notre implémentation s’aligne sur l’interface lisible.

Exemple 7: chaînage multiple de flux de transformation

Soyons la chaîne se transforme en texte de traitement en étapes: en majuscules, puis appliquez un horodatage. Dans src / chaintransform.ts:

import { createReadStream, createWriteStream } from 'fs';
import { Transform, TransformCallback } from 'stream';

class UppercaseTransform extends Transform {
  _transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
    this.push(chunk.toString().toUpperCase());
    callback();
  }
}

class TimestampTransform extends Transform {
  _transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
    const timestamp = new Date().toISOString();
    this.push(`[${timestamp}] ${chunk.toString()}`);
    callback();
  }
}

const readStream = createReadStream('data.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('output_chain.txt');
const upper = new UppercaseTransform();
const timestamp = new TimestampTransform();

readStream
  .pipe(upper)
  .pipe(timestamp)
  .pipe(writeStream)
  .on('finish', () => {
    console.log('Chained transform complete! Check output_chain.txt');
  })
  .on('error', (err: Error) => {
    console.error('Error:', err.message);
  });

Exécutez-le:

npx tsc && node dist/chainTransform.js

Cela lit data.txt, uppernases les données, ajoute un horodatage et écrit le résultat à output_chain.txt. Le chaînage se transforme présente la modularité des flux.

Meilleures pratiques pour les flux en dactylographie

  1. Tapez vos données: Définissez les interfaces pour les morceaux pour attraper les erreurs de type tôt.
  2. Gérer les erreurs: Attachez toujours les auditeurs d’événements d’erreur pour éviter les exceptions non gérées.
  3. Utilisez judicieusement les tuyaux: La tuyauterie réduit la gestion des événements manuels et améliore la lisibilité.
  4. Backpresure: Pour les grandes données, surveillez le WriteStream.WitableHighwatermark pour éviter de submerger la destination.

Cas d’utilisation du monde réel: Réponses API en streaming

Imaginez que vous construisez une API qui diffuse un grand ensemble de données. Utilisation d’express et de flux:

import express from 'express';
import { Readable } from 'stream';

const app = express();

app.get('/stream-data', (req, res) => {
  const data = ['Item 1\n', 'Item 2\n', 'Item 3\n'];
  const stream = Readable.from(data);

  res.setHeader('Content-Type', 'text/plain');
  stream.pipe(res);
});

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

Installez les dépendances (NPM Install Express @ Types / Express), puis exécutez-le. Visite http: // localhost: 3000 / stream-data Pour voir le flux de données dans votre navigateur!

Conseils avancés: manipulation de la contre-pression

Lorsqu’un flux écrit ne peut pas suivre un flux lisible, une contre-pression se produit. Node.js le gère automatiquement avec des tuyaux, mais vous pouvez le surveiller manuellement:

const writeStream = createWriteStream('large_output.txt');

if (!writeStream.write('data')) {
  console.log('Backpressure detected! Pausing...');
  writeStream.once('drain', () => {
    console.log('Resuming...');
  });
}

Cela garantit que votre application reste réactive sous des charges lourdes.

Précautions pour l’utilisation de la contre-pression: Lors de la rédaction de grandes quantités de données, le flux lisible peut produire des données plus rapidement que le flux écrivative ne peut les consommer. Alors que le tuyau et le pipeline le génèrent automatiquement, si l’écriture manuellement, vérifiez si write () renvoie False et attendez l’événement «drain» avant d’écrire davantage.

De plus, les itérateurs asynchronisés (pour attendre… de) sont des alternatives modernes pour la consommation de flux lisibles, ce qui peut souvent simplifier le code par rapport à l’utilisation .on («données») et .on («fin»).

Exemple:

async function processStream(readable: Readable) {
  for await (const chunk of readable) {
    console.log('Chunk:', chunk);
  }
  console.log('Finished reading.');
}

Points supplémentaires:

Assurer le nettoyage des ressources: Ceci est particulièrement important dans les implémentations de flux personnalisés ou lors de l’utilisation de Stream.pipeline. Appelez explicitement Stream.Destroy () dans les scénarios d’erreur ou lorsque le flux n’est plus nécessaire pour libérer des ressources sous-jacentes et empêcher les fuites. Stream.Pipeline les gère automatiquement pour les flux tuyaux.

Utilisez lisible.from () pour plus de commodité: Lorsque vous devez créer un flux à partir d’un itérable existant (comme un tableau) ou d’une approche asynchronisée, lisible.from () est souvent l’approche la plus simple et la plus moderne, nécessitant un code de passe-partout moins que de créer une classe lisible sur mesure.

Conclusion

Ruisseaux changent la donne dans Node.js, et TypeScript les améliore davantage en introduisant la sécurité et la clarté du type. De la lecture de fichiers à la transformation des données en temps réel, la maîtrise des flux ouvre un monde de possibilités d’E / S efficaces. Les exemples ici – lire, écrire, changer, comprimer et streaming sur HTTP – réprimande la surface de ce qui est possible.

Expérimentez avec vos propres pipelines: essayez de diffuser des journaux, de traitement des fichiers CSV ou de construire un système de chat en direct. Plus vous explorez, plus vous apprécierez la polyvalence des flux.




Source link