Node.js Stream-Modul

Java FileIntegrierte Module in Node.js


Stream ist eine abstrakte Schnittstelle in Node.js zur Verarbeitung von Streaming-Daten. Sie können sich einen Stream wie einen Wasserfluss vorstellen, bei dem Daten wie Wasser von einem Ort zum anderen fließen. Dieser Verarbeitungsansatz eignet sich besonders für große Dateien oder kontinuierliche Datenquellen, da nicht alle Daten auf einmal in den Speicher geladen werden müssen.

Warum brauchen wir Streams?

  1. Speichereffizienz: Beim Verarbeiten großer Dateien ist es nicht nötig, die gesamte Datei auf einmal in den Speicher zu laden
  2. Zeiteffizienz: Daten können verarbeitet werden, sobald sie empfangen werden, ohne darauf zu warten, dass alle Daten bereit sind
  3. Komponierbarkeit: Mehrere Streams können wie Pipes verbunden werden, um eine Datenverarbeitungs-Pipeline zu bilden

Vier grundlegende Arten von Streams

1. Lesbarer Stream

Ein lesbarer Stream ist die Datenquelle, aus der Daten gelesen werden können. Zum Beispiel das Lesen von Inhalten aus einer Datei oder das Abrufen von Daten aus einer HTTP-Anfrage.

Beispiel

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

readableStream.on('data', (chunk) => {
  console.log(`Received ${chunk.length}bytes of data`);
});

readableStream.on('end', () => {
  console.log('No more data');
});

2. Beschreibbarer Stream

Ein beschreibbarer Stream ist das Ziel von Daten, in das Daten geschrieben werden können. Zum Beispiel das Schreiben in eine Datei oder das Senden einer HTTP-Antwort.

Beispiel

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

writableStream.write('First line of data\n');
writableStream.write('Second line of data\n');
writableStream.end('Last line of data');

3. Duplex-Stream

Ein Duplex-Stream ist sowohl lesbar als auch beschreibbar, wie beispielsweise ein TCP-Socket.

Beispiel

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

const myDuplex = new Duplex({
  write(chunk, encoding, callback) {
    console.log(chunk.toString());
    callback();
  },
 
  read(size) {
    this.push('Aus dem Duplex-Stream gelesene Daten');
    this.push(null); // Kennzeichnet das Ende der Daten
  }
});

4. Transform-Stream

Ein Transform-Stream ist ein spezieller Typ eines Duplex-Streams, der Daten während des Lese- und Schreibvorgangs ändern oder transformieren kann. Zum Beispiel das Komprimieren oder Verschlüsseln von Daten.

Beispiel

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);

Gängige Methoden von Streams

pipe()-Methode

pipe()Die Methode ist eine der leistungsstärksten Funktionen von Stream; sie kann mehrere Streams miteinander verbinden, um eine Pipeline zu bilden.

Beispiel

const fs = require('fs');

// Einen lesbaren Stream und einen schreibbaren Stream erstellen
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');

// pipe verwenden, um den lesbaren Stream und den schreibbaren Stream zu verbinden
readStream.pipe(writeStream);

writeStream.on('finish', () => {
  console.log('Schreiben der Daten abgeschlossen');
});

Ereignisüberwachung

Stream ist eine Instanz von EventEmitter. Verschiedene Situationen können durch das Überwachen von Ereignissen behandelt werden:

  • data- Wird ausgelöst, wenn Daten zum Lesen verfügbar sind
  • end- Wird ausgelöst, wenn keine weiteren Daten mehr zum Lesen vorhanden sind
  • error- Wird ausgelöst, wenn ein Fehler auftritt
  • finish- Wird ausgelöst, wenn alle Daten in das zugrunde liegende System geschrieben wurden

Praktische Anwendungsbeispiele

Beispiel 1: Dateikopie

Beispiel

const fs = require('fs');

function copyFile(source, target, cb) {
  // Lese- und Schreib-Streams erstellen
  const rd = fs.createReadStream(source);
  const wr = fs.createWriteStream(target);

  // Fehler behandeln
  rd.on('error', err => cb(err));
  wr.on('error', err => cb(err));
 
  // Abschluss-Callback
  wr.on('finish', cb);
 
  // Kopieren starten
  rd.pipe(wr);
}

// Verwendung
copyFile('source.txt', 'target.txt', err => {
  if (err) console.error(err);
  else console.log('Kopieren abgeschlossen');
});

Beispiel 2: HTTP-Server komprimiert Dateien

Beispiel

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

http.createServer((req, res) => {
  // Einen lesbaren Stream erstellen
  const readStream = fs.createReadStream('./largeFile.txt');
 
  // Antwort-Header setzen
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Content-Encoding': 'gzip'
  });
 
  // Pipeline-Kette: lesen -> komprimieren -> antworten
  readStream.pipe(zlib.createGzip()).pipe(res);
}).listen(3000);

Best Practices für Streams

  1. Fehlerbehandlung: Fügen Sie Streams immer Fehlerbehandlungs-Listener hinzu
  2. Speicherverwaltung: Verwenden Sie für große Dateien Streams, anstatt alles auf einmal zu lesen
  3. Pipeline-Kette: Verwenden Sie die pipe()-Methode richtig, um mehrere Streams zu kombinieren
  4. Backpressure-Behandlung: Wenn die Schreibgeschwindigkeit nicht mit der Lesegeschwindigkeit mithalten kann, muss Backpressure angemessen behandelt werden

Beispiel für Fehlerbehandlung

Beispiel

const fs = require('fs');

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

readStream.on('error', (err) => {
  console.error('Fehler aufgetreten:', err.message);
});

Durch die Beherrschung des Node.js-Stream-Moduls können Sie verschiedene E/A-Operationen effizient ausführen. Besonders bei der Verarbeitung großer Dateien oder Echtzeitdaten werden Streams zu Ihrem leistungsstarken Werkzeug.

Java FileIntegrierte Module in Node.js

Andere Erweiterungen