Node.js-strømme: FileStream, Pipes og begivenheder

⚡ Smart opsummering

Node.js-streams læser og skriver data i små bidder i stedet for at indlæse hele filer i hukommelsen, og denne side forklarer oprettelse af filestreams, pipe chaining, håndtering af modtryk og hændelsesmodellen, der sikrer effektiv dataoverførsel i stor skala.

  • ???? Strømtyper: Node.js organiserer hver strøm som læsbar, skrivbar, duplex eller transformeret, og hver Node.js API returnerer en af ​​disse fire typer.
  • 📂 Filstrøm: fs.createReadStream() og fs.createWriteStream() læser og skriver filer i bidder uden at indlæse hele filen i hukommelsen.
  • 🔗 Piping: Pipe()-metoden forbinder en læsbar strøm direkte med en skrivbar strøm og flytter data automatisk mellem dem.
  • ⚖️ Rygpres: write() returnerer falsk, når bufferen er fuld, og drain-hændelsen signalerer, når det er sikkert at genoptage skrivningen.
  • 📡 Stream-begivenheder: data, end, error og finish markerer de vigtigste øjeblikke i en streams livscyklus og driver det meste af stream-håndteringskoden.
  • ⚡ Tilpassede begivenheder: Event-modulet og EventEmitter lader enhver Node.js-applikation definere, udsende og lytte efter sine egne navngivne events.
  • 🛠️ Lytterkontrol: once(), listenerCount() og newListener-begivenheden styrer, hvor mange gange en handler kører, og hvordan lyttere tracked.

Typer af strømme i Node.js

Hver stream, som Node.js eksponerer, falder ind under en af ​​fire basistyper, og det at vide, hvilken type en given API returnerer, forklarer, hvordan den stream opfører sig, før der skrives kode. Stream-modulet, der følger med Node.js, definerer disse kategorier, og de fil-, netværks- og komprimerings-API'er, der bruges i hele denne artikel, er alle bygget oven på dem.

Stream type Dataretning Eksempel-API Almindelig brug
Readable Kun kilde (data flyder ud) fs.createReadStream() Læsning af en fil, modtagelse af en HTTP-anmodningstekst
skrivbar Kun destination (data flyder ind) fs.createWriteStream() Skrivning af en fil, afsendelse af et HTTP-svar
Duplex Begge retninger, uafhængigt net.Socket (TCP-socket) Tovejs netværkskommunikation
Transform Begge retninger, data ændres undervejs zlib.createGzip() Komprimering eller kodning af data midt i processen

En læsbar strøm og en skrivbar strøm er de to byggesten, der bruges i hele denne artikel; begge driver også en HTTP webserver i Node.js. En duplex-strøm kombinerer begge roller i ét objekt, mens en transform-strøm er en duplex-strøm, hvis output ændrer dens input, såsom gzip-komprimering anvendt midt i strømmen.

Filstream i Node.js

Med de fire streamkategorier defineret, opbygger resten af ​​denne artikel en filoverførsel ved hjælp af de læsbare og skrivbare stream-API'er.

Node gør udstrakt brug af streams som en dataoverførselsmekanisme.

For eksempel, når du udsender noget til konsollen ved hjælp af console.log-funktionen, bruger du faktisk en stream til at sende dataene til konsollen.

Node.js har også mulighed for at streame data fra filer, så de kan læses og skrives korrekt. Vi vil nu se på et eksempel på, hvordan vi kan bruge streams til at læse og skrive fra filer. Vi skal følge nedenstående trin for dette eksempel

Trin 1) Opret en fil kaldet data.txt, som har nedenstående data. Lad os antage, at denne fil er gemt på D-drevet på vores lokale maskine.

Tutorial om Node.js

Introduktion

Events

Generators

Dataforbindelse

Bruger jasmin

Trin 2) Skriv den relevante kode, som vil gøre brug af streams til at læse data fra filen.

Filstream i Node.js

var fs = require("fs");
var stream;
stream = fs.createReadStream("D://data.txt");

stream.on("data", function(data) {
    var chunk = data.toString();
    console.log(chunk);
}); 

Code Forklaring:-

  1. Vi skal først inkludere 'fs'-modulerne, som indeholder al den funktionalitet, der kræves for at skabe streams.
  2. Dernæst opretter vi en læsbar stream ved at bruge metoden - createReadStream. Som input angiver vi placeringen af ​​vores data.txt-fil.
  3. Steam.on-funktionen er en hændelseshandler, og i den angiver vi den første parameter som 'data'. Dette betyder, at når der kommer data i strømmen fra filen, skal du udføre en tilbagekaldsfunktion. I vores tilfælde definerer vi en tilbagekaldsfunktion, som udfører 2 grundlæggende trin. Den første er at konvertere data læst fra filen som en streng. Det andet ville være at sende den konverterede streng som output til konsollen.
  4. Vi tager hver del af data, som læses fra datastrømmen, og konverterer den til en streng.
  5. Endelig sender vi output fra hver strengkonverteret del til konsollen.

Output:

Filstream i Node.js

  • Hvis koden udføres korrekt, vil du se ovenstående output i konsollen. Dette output vil være det samme som i data.txt-filen.

Skriver til en fil

På samme måde, som vi opretter en læsestrøm, kan vi også oprette en skrivestrøm for at skrive data til en fil. Lad os først oprette en tom fil uden indhold kaldet data.txt. Lad os antage, at denne fil er placeret i D-drevet på vores computer.

Nedenstående kode viser, hvordan vi kan skrive data til filen.

Filstream i Node.js

var fs = require("fs");
var stream;
stream = fs.createWriteStream("D://data.txt");

stream.write("Tutorial on Node.js")
stream.write("Introduction")
stream.write("Events")
stream.write("Generators")
stream.write("Data Connectivity")
stream.write("Using Jasmine") 

Code Forklaring:-

  1. Vi opretter en skrivbar stream ved at bruge metoden - createWriteStream. Som input angiver vi placeringen af ​​vores data.txt-fil.
  2. Dernæst brugte vi stream.write en metode til at skrive de forskellige tekstlinjer til vores tekstfil. Streamen sørger for at skrive disse data til data.txt-filen.

Hvis du åbner filen data.txt, vil du nu se følgende data i filen

Tutorial om Node.js

Introduktion

Events

Generators

Dataforbindelse

Ved brug af Jasmine

Rør i Node.js

Det er nyttigt at læse og skrive streams uafhængigt, men Node.js tillader også en læsbar stream at oprette direkte forbindelse til en skrivbar stream og flytte data uden manuel bufferingkode.

Inden for Node-applikationer kan streams kobles sammen ved hjælp af pipe()-metoden, som tager to argumenter:

  • En påkrævet skrivbar stream, der fungerer som destination for data og
  • Et valgfrit objekt, der bruges til at sende optioner ind.

Et typisk eksempel på brug af rør, hvis du ønsker at overføre data fra den ene fil til den anden.

Så lad os se et eksempel på, hvordan vi kan overføre data fra den ene fil til den anden ved hjælp af rør.

Trin 1) Opret en fil kaldet datainput.txt, som har nedenstående data. Lad os antage, at denne fil er gemt på D-drevet på vores lokale maskine.

Tutorial om Node.js

Introduktion

Events

Generators

Dataforbindelse

Bruger jasmin

Trin 2) Opret en tom tom fil kaldet dataOutput.txt og placer den på D-drevet på din lokale maskine.

Trin 3) Skriv nedenstående kode for at udføre overførslen af ​​data fra filen datainput.txt til filen dataOutput.txt.

Rør i Node.js

var fs = require("fs");
var readStream = fs.createReadStream("D://datainput.txt");
var writeStream = fs.createWriteStream("D://dataOutput.txt");
readStream.pipe(writeStream);

Code Forklaring:-

  1. Vi laver først en "readstream" til vores datainput.txt fil, som indeholder alle vores data, som skal overføres til den nye fil.
  2. Vi skal derefter oprette en "writestream" til vores dataOutput.txt-fil, som er vores tomme fil og er destinationen for overførsel af data fra datainput.txt-filen.
  3. Vi bruger derefter pipe-kommandoen til at overføre data fra læsestrømmen til skrivestrømmen. Pipe-kommandoen vil tage alle de data, der kommer ind i læsestrømmen, og skubbe den til skrivestrømmen.

Hvis du nu åbner filen dataOutput.txt, vil du se alle de data, som var til stede i filen datainput.txt.

Streamhændelser og modtryk i Node.js

Kald af pipe(), som vist ovenfor, skjuler to mekanismer, der er vigtige i stor skala: de hændelser, som hver strøm udsender, og modtrykssystemet, der forhindrer en hurtig kilde i at overvælde en langsom destination.

  • data: udsendes af en læsbar strøm hver gang en ny chunk er tilgængelig.
  • ende: udsendes, når en læsbar strøm har leveret sin sidste chunk.
  • Afslut: udsendes af en skrivbar strøm, når alle data i køen er ryddet.
  • fejl: udsendes af begge strømtyper ved fejl; håndter det altid, ellers går processen ned.

Eksemplet nedenfor lytter efter alle fire hændelser, mens modtrykket styres manuelt, hvilket er den samme funktion som pipe() automatisk udfører.

var fs = require("fs");
var readStream = fs.createReadStream("D://data.txt");
var writeStream = fs.createWriteStream("D://dataOutput.txt");

readStream.on("data", function(chunk) {
    var ok = writeStream.write(chunk);
    if (!ok) {
        readStream.pause();
    }
});

writeStream.on("drain", function() {
    readStream.resume();
});

readStream.on("end", function() {
    writeStream.end();
});

writeStream.on("finish", function() {
    console.log("Write completed.");
});

readStream.on("error", function(err) {
    console.log(err);
});

Modtryk opstår, når en læsbar strøm producerer data hurtigere end en skrivbar strøm kan forbruge den. write()-metoden returnerer falsk, når dens buffer er fyldt op, så ovenstående kode sætter kilden på pause, indtil destinationen udsender en drain-hændelse, hvilket signalerer, at det er sikkert at genoptage. pipe() udfører denne cyklus automatisk.

⚠ Tip: I produktionsfasen foretrækkes pipeline()-funktionen fra stream-modulet frem for en manuel pipe()-kæde, da den videresender fejl fra hver stream og lukker filhandles automatisk.

Begivenheder i Node.js

Streams er et eksempel på Node.js' bredere event-drevne design. Resten af ​​denne artikel ser på, hvordan events fungerer generelt, ved hjælp af den samme EventEmitter-klasse, som streams er afhængige af internt.

Hændelser er et af nøglebegreberne i Node.js, og nogle gange omtales Node.js som en begivenhedsdrevet ramme.

Grundlæggende er en begivenhed noget, der sker. For eksempel, hvis der oprettes en forbindelse til en database, udløses databaseforbindelseshændelsen. Hændelsesdrevet programmering er at skabe funktioner, der vil blive udløst, når specifikke hændelser udløses.

Lad os se på et grundlæggende eksempel på at definere en begivenhed i Node.js.

Vi vil oprette en begivenhed kaldet 'data_received'. Når denne hændelse udløses, vil teksten "data modtaget" blive sendt til konsollen.

Begivenheder i Node.js

var events = require('events');
var eventEmitter = new events.EventEmitter();
eventEmitter.on('data_received', function() {
    console.log('data received succesfully.');
});

eventEmitter.emit('data_received'); 

Code Forklaring:-

  1. Brug kræve funktionen til at inkludere 'begivenheder' modulet. Med dette modul vil du være i stand til at oprette begivenheder i Node.js.
  2. Opret en ny begivenhedsudsender. Dette bruges til at binde hændelsen, som i vores tilfælde er "data_received" til en tilbagekaldsfunktion, som er defineret i step3.
  3. Vi definerer en hændelsesdrevet funktion, som siger, at hvis "data_received"-hændelsen udløses, skal vi udsende teksten "data_received" til konsollen.
  4. Endelig har vi en manuel udløsning af vores begivenhed ved hjælp af eventEmiter.emit-funktionen. Dette udløser hændelsen data_received.

Når programmet køres, vil teksten "data modtaget" blive sendt til konsollen som vist nedenfor.

Begivenheder i Node.js

Udsender begivenheder

Ud over at sende og lytte efter hændelser, eksponerer Node.js ekstra metoder til at kontrollere, hvordan lyttere registreres og inspiceres.

Når begivenheder defineres, er der forskellige metoder til begivenheder, som kan påberåbes. Dette emne fokuserer på at se på hver enkelt af dem i detaljer.

  1. Engangshændelseshandlere

Nogle gange kan du være interesseret i kun at reagere på en begivenhed første gang den indtræffer. I disse situationer kan du bruge once() metoden.

Lad os se, hvordan vi kan gøre brug af once-metoden til hændelseshandlere.

Udsender begivenheder

Code Forklaring:-

  1. Her bruger vi 'once'-metoden til at sige, at for hændelsen 'data_received' skal tilbagekaldsfunktionen kun udføres én gang.
  2. Her udløser vi manuelt hændelsen 'data_received'.
  3. Når hændelsen 'data_received' udløses igen, vil der ikke ske noget denne gang. Dette er på grund af det første trin, hvor vi sagde, at begivenheden kun kunne udløses én gang.

Hvis koden udføres korrekt, vil outputtet i loggen være 'data_received successfully'. Denne besked vises kun én gang i konsollen.

  1. Inspicering af begivenhedslyttere

På ethvert tidspunkt i sin levetid kan en begivenhedsudsender have nul eller flere lyttere knyttet til sig. Lytterne for hver begivenhedstype kan inspiceres på flere måder.

Hvis du kun er interesseret i at bestemme antallet af tilknyttede lyttere, skal du ikke lede længere end til EventEmitter.listenerCount()-metoden.

(Bemærk: Lytterne er vigtige, fordi hovedprogrammet skal vide, om lyttere bliver tilføjet til en begivenhed, ellers vil programmet ikke fungere, fordi yderligere lyttere bliver kaldt.)

Udsender begivenheder

Code Forklaring:-

  1. Vi definerer en eventEmitter-type, som er nødvendig for at bruge de hændelsesrelaterede metoder.
  2. Vi definerer derefter et objekt kaldet emitter, som vil blive brugt til at definere vores hændelseshandlere.
  3. Vi opretter 2 hændelseshandlere, som stort set ikke gør noget. Dette er holdt simpelt for vores eksempel bare for at vise, hvordan listenerCount-metoden fungerer.
  4. Når du nu påkalder listenerCount-metoden på vores data_received-hændelse, vil den sende antallet af hændelseslyttere knyttet til denne hændelse i konsolloggen.

Hvis koden udføres korrekt, vil værdien 2 blive vist i konsolloggen.

  1. Den nyeListener Event

Hver gang en ny hændelseshandler registreres, udsender hændelsessenderen en newListener hændelse. Denne hændelse bruges til at opdage nye hændelseshandlere. Du bruger typisk newListener-hændelse, når du skal allokere ressourcer eller udføre en handling for hver ny hændelseshandler.

Udsender begivenheder

var events = require('events');
var eventEmitter = events.EventEmitter;
var emitter = new eventEmitter();
emitter.on("newListener", function(eventName, listener) {
    console.log("Added listener for " + eventName + " events");
});
emitter.on('data_received', function() {});
emitter.on('data_received', function() {}); 

Code Forklaring:-

  1. Vi er ved at oprette en ny hændelseshandler til 'newListener'-begivenheden. Så hver gang en ny hændelseshandler er registreret, vil teksten "Tilføjet lytter til" + hændelsesnavnet blive vist i konsollen.
  2. Her skriver vi til konsollen teksten "Tilføjet lytter til" + begivenhedsnavnet for hver registreret begivenhed.
  3. Vi definerer 2 hændelseshandlere for vores hændelse 'data_received'.

Hvis ovenstående kode udføres korrekt, vil nedenstående tekst blive vist i konsollen. Det viser bare, at 'newListener'-hændelseshandleren blev udløst to gange.

Tilføjet lytter til data_received begivenheder

Tilføjet lytter til data_received begivenheder

Ofte Stillede Spørgsmål

fs.readFile() indlæser en hel fil i hukommelsen, før den returneres, mens fs.createReadStream() leverer filen i små bidder, når de ankommer. Streams bruger langt mindre hukommelse til store filer og lader behandlingen starte, før læsningen er færdig.

Ja. Kald .pipe() én gang pr. destination, f.eks. readStream.pipe(destinationA) efterfulgt af readStream.pipe(destinationB). Node.js leverer de samme chunks til hver destination, og hver skrivbar stream styrer stadig sit eget modtryk uafhængigt.

Ja. Send en læsbar strøm gennem en transform-strøm, f.eks. zlib.createGzip(), og derefter ind i en skrivbar strøm: readStream.pipe(zlib.createGzip()).pipe(writeStream). Filen komprimeres, mens den streames, uden nogensinde at indlæse hele filen i hukommelsen.

De kan hurtigt udarbejde fungerende readStream og pipe-standardkoder, men de udelader ofte fejlhændelseshåndteringen og modtrykshåndteringen. Gennemgå altid AI-genereret streamkode for begge huller, før den bruges i produktion.

De kan foreslå sandsynlige årsager, såsom en manglende drain-lytter eller en uhåndteret fejlhændelse, men bekræftelse af den virkelige årsag kræver stadig manuel kontrol af streamtilstand og applikationslogfiler.

Opsummer dette indlæg med: