Node.js-streams: FileStream, Pipes en Events
โก Slimme samenvatting
Node.js-streams lezen en schrijven gegevens in kleine stukjes in plaats van hele bestanden in het geheugen te laden. Deze pagina legt uit hoe je een bestandsstream aanmaakt, pijplijnen koppelt, backpressure afhandelt en het gebeurtenismodel gebruikt dat zorgt voor efficiรซnte gegevensoverdracht op grote schaal.

Soorten streams in Node.js
Elke stream die Node.js beschikbaar stelt, valt in een van de vier basistypen. Weten welk type een bepaalde API retourneert, verklaart hoe die stream zich gedraagt โโvoordat er code wordt geschreven. De stream-module die met Node.js wordt meegeleverd, definieert deze categorieรซn, en de API's voor bestanden, netwerken en compressie die in dit artikel worden gebruikt, zijn daarop gebouwd.
| Stroomtype | Gegevensrichting | Voorbeeld-API | Veelvoorkomend gebruiksscenario |
|---|---|---|---|
| Readable | Alleen bron (data stroomt naar buiten) | fs.createReadStream() | Een bestand lezen en een HTTP-verzoekbody ontvangen. |
| Beschrijfbaar | Alleen bestemming (gegevensstromen komen binnen) | fs.createWriteStream() | Een bestand schrijven, een HTTP-antwoord verzenden |
| Duplex | Beide richtingen, onafhankelijk van elkaar. | net.Socket (TCP-socket) | Tweewegs netwerkcommunicatie |
| Transformeren | In beide richtingen worden de gegevens tijdens de overdracht gewijzigd. | zlib.createGzip() | Gegevens comprimeren of coderen tijdens het verwerkingsproces. |
Een leesbare stream en een schrijfbare stream zijn de twee bouwstenen die in dit artikel worden gebruikt; beide vormen ook de basis voor een HTTP-webserver In Node.js combineert een duplexstream beide rollen in รฉรฉn object, terwijl een transformstream een โโduplexstream is waarvan de uitvoer de invoer wijzigt, zoals bijvoorbeeld gzip-compressie die halverwege de stream wordt toegepast.
Filestream in Node.js
Nu de vier streamcategorieรซn zijn gedefinieerd, wordt in de rest van dit artikel een bestandsoverdracht opgebouwd met behulp van de API's voor leesbare en beschrijfbare streams.
Node maakt uitgebreid gebruik van streams als mechanisme voor gegevensoverdracht.
Wanneer u bijvoorbeeld iets naar de console uitvoert met behulp van de console.log-functie, gebruikt u feitelijk een stream om de gegevens naar de console te verzenden.
Node.js heeft ook de mogelijkheid om gegevens uit bestanden te streamen, zodat deze op de juiste manier kunnen worden gelezen en geschreven. We zullen nu kijken naar een voorbeeld van hoe we streams kunnen gebruiken om vanuit bestanden te lezen en te schrijven. Voor dit voorbeeld moeten we de onderstaande stappen volgen
Stap 1) Maak een bestand met de naam data.txt dat de onderstaande gegevens bevat. Laten we aannemen dat dit bestand is opgeslagen op de D-schijf van onze lokale machine.
Zelfstudie over Node.js
Introductie
Evenementen
Data connectiviteit
Jasmijn gebruiken
Stap 2) Schrijf de relevante code die gebruik maakt van streams om gegevens uit het bestand te lezen.
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 Uitleg:-
- We moeten eerst de 'fs'-modules toevoegen die alle functionaliteit bevatten die nodig is om streams te maken.
- Vervolgens maken we een leesbare stream met behulp van de methode โ createReadStream. Als invoer geven we de locatie van ons data.txt-bestand.
- De steam.on-functie is een gebeurtenishandler en daarin specificeren we de eerste parameter als 'data'. Dit betekent dat wanneer er gegevens uit het bestand in de stroom komen, er een callback-functie wordt uitgevoerd. In ons geval definiรซren we een callback-functie die twee basisstappen uitvoert. De eerste is om de uit het bestand gelezen gegevens als een string te converteren. De tweede zou zijn om de geconverteerde string als uitvoer naar de console te sturen.
- We nemen elk stuk gegevens dat uit de datastroom wordt gelezen en converteren het naar een string.
- Ten slotte sturen we de uitvoer van elk string-geconverteerd deel naar de console.
Output:
- Als de code correct wordt uitgevoerd, ziet u de bovenstaande uitvoer in de console. Deze uitvoer is dezelfde als die in het data.txt-bestand.
Schrijven naar een bestand
Op dezelfde manier waarop we een leesstroom maken, kunnen we ook een schrijfstroom maken om gegevens naar een bestand te schrijven. Laten we eerst een leeg bestand maken zonder inhoud, genaamd data.txt. Laten we aannemen dat dit bestand op de D-schijf van onze computer is geplaatst.
De onderstaande code laat zien hoe we gegevens naar het bestand kunnen schrijven.
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 Uitleg:-
- We maken een beschrijfbare stream met behulp van de methode createWriteStream. Als invoer geven we de locatie van ons data.txt-bestand.
- Vervolgens gebruikten we de stream.write een methode om de verschillende regels tekst naar ons tekstbestand te schrijven. De stream zorgt ervoor dat deze gegevens naar het data.txt-bestand worden geschreven.
Als u het bestand data.txt opent, ziet u nu de volgende gegevens in het bestand
Zelfstudie over Node.js
Introductie
Evenementen
Generators
Data connectiviteit
gebruik Jasmijn
Leidingen in Node.js
Het onafhankelijk lezen en schrijven van streams is handig, maar Node.js maakt het ook mogelijk om een โโleesbare stream rechtstreeks te verbinden met een schrijfbare stream, waardoor gegevens kunnen worden overgedragen zonder handmatige buffering.
Binnen Node-toepassingen kunnen streams aan elkaar worden gekoppeld met behulp van de pipe()-methode, waarvoor twee argumenten nodig zijn:
- Een vereiste beschrijfbare stream die fungeert als bestemming voor de gegevens en
- Een optioneel object dat wordt gebruikt om opties door te geven.
Een typisch voorbeeld van het gebruik van pipelines, als u gegevens van het ene bestand naar het andere wilt overbrengen.
Laten we dus een voorbeeld bekijken van hoe we gegevens van het ene bestand naar het andere kunnen overbrengen met behulp van pipelines.
Stap 1) Maak een bestand met de naam datainput.txt dat de onderstaande gegevens bevat. Laten we aannemen dat dit bestand is opgeslagen op de D-schijf van onze lokale machine.
Zelfstudie over Node.js
Introductie
Evenementen
Generators
Data connectiviteit
Jasmijn gebruiken
Stap 2) Maak een leeg leeg bestand met de naam dataOutput.txt en plaats dit op de D-schijf van uw lokale machine.
Stap 3) Schrijf de onderstaande code om de gegevensoverdracht van het datainput.txt-bestand naar het dataOutput.txt-bestand uit te voeren.
var fs = require("fs"); var readStream = fs.createReadStream("D://datainput.txt"); var writeStream = fs.createWriteStream("D://dataOutput.txt"); readStream.pipe(writeStream);
Code Uitleg:-
- We maken eerst een โreadstreamโ naar ons datainput.txt-bestand dat al onze gegevens bevat die naar het nieuwe bestand moeten worden overgebracht.
- We moeten dan een โwritestreamโ maken naar ons dataOutput.txt-bestand, wat ons lege bestand is en de bestemming is voor de overdracht van gegevens uit het datainput.txt-bestand.
- Vervolgens gebruiken we het pipe-commando om de gegevens van de leesstroom naar de schrijfstroom over te brengen. Het pipe-commando neemt alle gegevens die in de leesstroom binnenkomen en duwt deze naar de schrijfstroom.
Als u nu het bestand dataOutput.txt opent, ziet u alle gegevens die in het bestand datainput.txt aanwezig waren.
Streamgebeurtenissen en tegendruk in Node.js
Door pipe() aan te roepen, zoals hierboven weergegeven, worden twee mechanismen verborgen die op grote schaal van belang zijn: de gebeurtenissen die elke stream genereert, en het tegendruksysteem dat voorkomt dat een snelle bron een langzame bestemming overweldigt.
- data: wordt door een leesbare stream uitgezonden telkens wanneer een nieuw gegevensblok beschikbaar is.
- einde: wordt uitgezonden zodra een leesbare stream zijn laatste deel heeft afgeleverd.
- af hebben: wordt door een beschrijfbare stream verzonden zodra alle in de wachtrij geplaatste gegevens zijn verwerkt.
- fout: Deze foutmelding wordt door beide streamtypen gegenereerd bij een fout; handel deze altijd af, anders crasht het proces.
Het onderstaande voorbeeld luistert naar alle vier de gebeurtenissen en beheert handmatig de tegendruk, hetzelfde gedrag dat pipe() automatisch biedt.
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); });
Backpressure treedt op wanneer een leesbare stream sneller data produceert dan een schrijfbare stream deze kan verwerken. De write()-methode retourneert false zodra de buffer vol is, dus de bovenstaande code pauzeert de bron totdat de bestemming een drain-event uitzendt, wat aangeeft dat het veilig is om verder te gaan. pipe() voert deze cyclus automatisch uit.
โ Tip: In een productieomgeving is het aan te raden de `pipeline()`-functie uit de `stream`-module te gebruiken in plaats van een handmatige `pipe()`-keten, omdat deze fouten van elke stream doorstuurt en bestandshendels automatisch sluit.
Gebeurtenissen in Node.js
Streams zijn een voorbeeld van Node.js' bredere, op gebeurtenissen gebaseerde ontwerp. De rest van dit artikel gaat dieper in op hoe gebeurtenissen in het algemeen werken, waarbij gebruik wordt gemaakt van dezelfde EventEmitter-klasse waarop streams intern gebaseerd zijn.
Gebeurtenissen zijn een van de belangrijkste concepten in Node.js. Soms wordt Node.js ook wel een gebeurtenisgestuurd framework genoemd.
Kortom, een gebeurtenis is iets dat gebeurt. Als er bijvoorbeeld een verbinding met een database tot stand wordt gebracht, wordt de databaseverbindingsgebeurtenis geactiveerd. Gebeurtenisgestuurd programmeren is het creรซren van functies die worden geactiveerd wanneer specifieke gebeurtenissen worden geactiveerd.
Laten we eens kijken naar een eenvoudig voorbeeld van het definiรซren van een gebeurtenis in Node.js.
We gaan een gebeurtenis maken met de naam 'data_received'. Wanneer deze gebeurtenis wordt geactiveerd, wordt de tekst โdata ontvangenโ naar de console verzonden.
var events = require('events'); var eventEmitter = new events.EventEmitter(); eventEmitter.on('data_received', function() { console.log('data received succesfully.'); }); eventEmitter.emit('data_received');
Code Uitleg:-
- Gebruik de functie require om de module 'evenementen' op te nemen. Met deze module kunt u evenementen maken in Node.js.
- Maak een nieuwe gebeurtenissenzender. Dit wordt gebruikt om de gebeurtenis, die in ons geval โdata_receivedโ is, te binden aan een callback-functie die is gedefinieerd in stap 3.
- We definiรซren een gebeurtenisgestuurde functie die zegt dat als de gebeurtenis โdata_receivedโ wordt geactiveerd, we de tekst โdata_receivedโ naar de console moeten uitvoeren.
- Ten slotte hebben we een handmatige trigger van onze gebeurtenis met behulp van de functie eventEmiter.emit. Hierdoor wordt de gebeurtenis data_received geactiveerd.
Wanneer het programma wordt uitgevoerd, wordt de tekst โdata ontvangenโ naar de console verzonden, zoals hieronder weergegeven.
Gebeurtenissen uitzenden
Naast het uitzenden en ontvangen van gebeurtenissen, biedt Node.js extra methoden om te bepalen hoe luisteraars worden geregistreerd en gecontroleerd.
Bij het definiรซren van gebeurtenissen zijn er verschillende methoden voor gebeurtenissen die kunnen worden aangeroepen. Dit onderwerp richt zich op het in detail bekijken van elk van hen.
- Eenmalige gebeurtenishandlers
Soms bent u er wellicht alleen in geรฏnteresseerd om op een gebeurtenis te reageren als deze zich voor de eerste keer voordoet. In deze situaties kunt u de methode once() gebruiken.
Laten we eens kijken hoe we de eenmalige methode voor gebeurtenishandlers kunnen gebruiken.
Code Uitleg:-
- Hier gebruiken we de 'once'-methode om te zeggen dat voor de gebeurtenis 'data_received' de callback-functie slechts รฉรฉn keer mag worden uitgevoerd.
- Hier activeren we handmatig de gebeurtenis 'data_received'.
- Wanneer de gebeurtenis 'data_received' opnieuw wordt geactiveerd, gebeurt er deze keer niets. Dit komt door de eerste stap waarin we zeiden dat de gebeurtenis maar รฉรฉn keer kon worden geactiveerd.
Als de code correct wordt uitgevoerd, is de uitvoer in het logboek 'data_received Successful'. Dit bericht verschijnt slechts รฉรฉn keer in de console.
- Gebeurtenislisteners inspecteren
Op elk moment in zijn levensduur kunnen aan een gebeurteniszender nul of meer luisteraars zijn gekoppeld. De luisteraars voor elk gebeurtenistype kunnen op verschillende manieren worden geรฏnspecteerd.
Als u alleen het aantal gekoppelde luisteraars wilt bepalen, hoeft u niet verder te zoeken dan de methode EventEmitter.listenerCount().
(Let op: Luisteraars zijn belangrijk omdat het hoofdprogramma moet weten of er luisteraars direct aan een evenement worden toegevoegd, anders werkt het programma niet goed omdat er extra luisteraars worden gebeld.)
Code Uitleg:-
- We definiรซren een eventEmitter-type dat vereist is voor het gebruik van de gebeurtenisgerelateerde methoden.
- Vervolgens definiรซren we een object met de naam emitter dat zal worden gebruikt om onze gebeurtenishandlers te definiรซren.
- We maken twee gebeurtenishandlers die in principe niets doen. Dit wordt voor ons voorbeeld eenvoudig gehouden om te laten zien hoe de methode listenerCount werkt.
- Wanneer u nu de methode listenerCount aanroept voor onze gebeurtenis data_received, wordt het aantal gebeurtenislisteners verzonden dat aan deze gebeurtenis is gekoppeld in het consolelogboek.
Als de code correct wordt uitgevoerd, wordt de waarde 2 weergegeven in het consolelogboek.
- Het nieuweListener-evenement
Elke keer dat een nieuwe gebeurtenishandler wordt geregistreerd, verzendt de gebeurteniszender een newListener-gebeurtenis. Deze gebeurtenis wordt gebruikt om nieuwe gebeurtenishandlers te detecteren. Normaal gesproken gebruikt u de gebeurtenis newListener wanneer u bronnen moet toewijzen of een actie moet uitvoeren voor elke nieuwe gebeurtenishandler.
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 Uitleg:-
- We maken een nieuwe event handler voor de 'newListener'-gebeurtenis. Dus wanneer een nieuwe event handler wordt geregistreerd, wordt de tekst 'Added listener for' + de eventnaam weergegeven in de console.
- Hier schrijven we naar de console de tekst "Listener toegevoegd voor" + de gebeurtenisnaam voor elke geregistreerde gebeurtenis.
- We definiรซren 2 gebeurtenishandlers voor onze gebeurtenis 'data_received'.
Als de bovenstaande code correct wordt uitgevoerd, wordt de onderstaande tekst in de console weergegeven. Het laat alleen zien dat de gebeurtenishandler 'newListener' twee keer is geactiveerd.
Luisteraar toegevoegd voor data_received-gebeurtenissen
Luisteraar toegevoegd voor data_received-gebeurtenissen









