Node.js Stream-Modul
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?
- Speichereffizienz: Beim Verarbeiten großer Dateien ist es nicht nötig, die gesamte Datei auf einmal in den Speicher zu laden
- Zeiteffizienz: Daten können verarbeitet werden, sobald sie empfangen werden, ohne darauf zu warten, dass alle Daten bereit sind
- 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 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 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 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 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
// 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 sindend- Wird ausgelöst, wenn keine weiteren Daten mehr zum Lesen vorhanden sinderror- Wird ausgelöst, wenn ein Fehler auftrittfinish- Wird ausgelöst, wenn alle Daten in das zugrunde liegende System geschrieben wurden
Praktische Anwendungsbeispiele
Beispiel 1: Dateikopie
Beispiel
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 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
- Fehlerbehandlung: Fügen Sie Streams immer Fehlerbehandlungs-Listener hinzu
- Speicherverwaltung: Verwenden Sie für große Dateien Streams, anstatt alles auf einmal zu lesen
- Pipeline-Kette: Verwenden Sie die pipe()-Methode richtig, um mehrere Streams zu kombinieren
- Backpressure-Behandlung: Wenn die Schreibgeschwindigkeit nicht mit der Lesegeschwindigkeit mithalten kann, muss Backpressure angemessen behandelt werden
Beispiel für Fehlerbehandlung
Beispiel
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.
Andere Erweiterungen
Integrierte Module in Node.js