Streams in Node.js kunnen ingewikkeld zijn, maar het is de moeite waard om de tijd te nemen om ze te begrijpen.

Belangrijkste leerpunten

  • Streams in Node.js zijn een fundamenteel hulpmiddel voor gegevensverwerking en -overdracht, waardoor ze ideaal zijn voor realtime en gebeurtenisgestuurde toepassingen.
  • Om een ​​beschrijfbare stream in Node.js te maken, kunt u de createWriteStream()-functie van de fs-module gebruiken, die gegevens naar een specifieke locatie schrijft.
  • Leesbaar, beschrijfbaar, duplex en transformatie zijn de vier soorten streams in Node.js, elk met zijn eigen gebruiksscenario en functionaliteit.

Een stream is een fundamenteel programmeerhulpmiddel dat zich bezighoudt met de gegevensstroom. In de kern vertegenwoordigt een stroom doorgaans de opeenvolgende overdracht van bytes van het ene punt naar het andere. De officiële documentatie van Node.js definieert een stream als een abstracte interface die u kunt gebruiken om met gegevens te werken.

Het overbrengen van gegevens op een computer of via een netwerk is een ideaal gebruik van een stream.

instagram viewer

Streams in Node.js

Streams hebben een essentiële rol gespeeld in het succes van Node.js. Ze zijn ideaal voor realtime gegevensverwerking en gebeurtenisgestuurde toepassingen, twee opvallende kenmerken van de Node.js-runtimeomgeving.

Om een ​​nieuwe stream in Node.js te maken, moet je de stream-API gebruiken, die uitsluitend werkt met Strings en Node.js buffergegevens. Node.js heeft vier soorten streams: beschrijfbaar, leesbaar, duplex en transformatie.

Een beschrijfbare stream maken en gebruiken

Met een beschrijfbare stream kunt u gegevens naar een specifieke locatie schrijven of verzenden. De fs-module (bestandssysteem) heeft een WriteStream-klasse, die u kunt gebruiken om een ​​nieuwe stream te maken met de fs.createWriteStream() functie. Deze functie accepteert het pad naar het bestand waarnaar u gegevens wilt schrijven, evenals een optionele reeks opties.

const {createWriteStream} = require("fs");

(() => {
const file = "myFile.txt";
const myWriteStream = createWriteStream(file);
let x = 0;
const writeNumber = 10000;

const writeData = () => {
while (x < writeNumber) {
const chunk = Buffer.from(`${x}, `, "utf-8");
if (x writeNumber - 1) return myWriteStream.end(chunk);
if (!myWriteStream.write(chunk)) break;
x++
}
};

writeData();
})();

Deze code importeert de createWriteStream() functie, welke de anonieme pijlfunctie wordt vervolgens gebruikt om een ​​stream te maken die gegevens naar myFile.txt schrijft. De anonieme functie bevat een innerlijke functie genaamd schrijfgegevens() die gegevens schrijft.

De createWriteStream() De functie werkt met een buffer om een ​​verzameling getallen (0–9.999) naar het doelbestand te schrijven. Wanneer u echter het bovenstaande script uitvoert, wordt er in dezelfde map een bestand gemaakt dat de volgende gegevens bevat:

De huidige verzameling getallen eindigt op 2.915, maar had nummers tot en met 9.999 moeten bevatten. Deze discrepantie treedt op omdat elke WriteStream een ​​buffer gebruikt die een vaste hoeveelheid gegevens tegelijk opslaat. Om te weten wat deze standaardwaarde is, moet u de hoogWaterMark keuze.

console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");

Het toevoegen van de bovenstaande coderegel aan de anonieme functie levert de volgende uitvoer op in de terminal:

De terminaluitvoer laat zien dat de standaard hoogWaterMark waarde (die aanpasbaar is) is 16.384 bytes. Dit betekent dat u in deze buffer slechts minder dan 16.384 bytes aan gegevens tegelijk kunt opslaan. Dus tot en met nummer 2.915 (plus alle komma's en spaties) vertegenwoordigt de maximale hoeveelheid gegevens die de buffer in één keer kan opslaan.

De oplossing voor de bufferfout is het gebruik van een streamgebeurtenis. Een stream komt verschillende gebeurtenissen tegen in verschillende stadia van het gegevensoverdrachtproces. De droogleggen evenement is de geschikte optie voor deze situatie.

In de schrijfgegevens() functie hierboven, de oproep naar de WriteStream's write() functie retourneert true als het gegevensfragment (of de interne buffer) lager is dan de hoogWaterMark waarde. Dit geeft aan dat de applicatie meer gegevens naar de stream kan sturen. Echter, zodra de schrijven() functie retourneert false, de lus wordt verbroken omdat u de buffer moet leegmaken.

myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});

Het invoegen van de droogleggen gebeurteniscode hierboven in de anonieme functie zal de WriteStream-buffer wanneer deze op capaciteit is. Vervolgens herinnert het zich de schrijfgegevens() methode, zodat het kan doorgaan met het schrijven van gegevens. Het uitvoeren van de bijgewerkte applicatie levert de volgende uitvoer op:

Houd er rekening mee dat de toepassing het WriteStream-buffer drie keer tijdens de uitvoering ervan. Het tekstbestand ondervond ook enkele wijzigingen:

Een leesbare stream maken en gebruiken

Om gegevens te lezen, begint u met het maken van een leesbare stream met behulp van de fs.createReadStream() functie.

const {createReadStream} = require("fs");

(() => {
const file = "myFile.txt";
const myReadStream = createReadStream(file);

myReadStream.on("open", () => {
console.log(`The read stream has successfully opened ${file}.`);
});

myReadStream.on("data", chunk => {
console.log("The file contains the following data: " + chunk.toString());
});

myReadStream.on("close", () => {
console.log("The file has been successfully closed.");
});
})();

Het bovenstaande script gebruikt de createReadStream() methode om toegang te krijgen tot het bestand dat met de vorige code is gemaakt: myFile.txt. De createReadStream() functie accepteert een bestandspad (dat de vorm kan hebben van een tekenreeks, buffer of URL) en verschillende optionele opties als argumenten.

In de anonieme functie zijn er verschillende belangrijke streamgebeurtenissen. Er is echter geen teken van de droogleggen evenement. Dit komt omdat een leesbare stream alleen gegevens buffert wanneer u de stream.push (stukje) functie of gebruik de leesbaar evenement.

De open gebeurtenis wordt geactiveerd wanneer fs het bestand opent waarvan u wilt lezen. Wanneer u de gegevens Wanneer een gebeurtenis wordt omgezet in een impliciet continue stroom, zorgt dit ervoor dat de stroom overgaat in een stromende modus. Hierdoor kunnen gegevens worden doorgegeven zodra deze beschikbaar komen. Het uitvoeren van de bovenstaande applicatie levert de volgende uitvoer op:

Een duplexstream maken en gebruiken

Een duplexstream implementeert zowel de beschrijfbare als de leesbare streaminterfaces, zodat u zo'n stream kunt lezen en ernaar kunt schrijven. Een voorbeeld is een TCP-socket die voor zijn creatie afhankelijk is van de netmodule.

Een eenvoudige manier om de eigenschappen van een duplexstream te demonstreren is door een TCP-server en -client te maken die gegevens overdraagt.

Het server.js-bestand

const net = require('net');
const port = 5000;
const host = '127.0.0.1';

const server = net.createServer();

server.on('connection', (socket)=> {
console.log('Connection established from client.');

socket.on('data', (data) => {
console.log(data.toString());
});

socket.write("Hi client, I am server " + server.address().address);

socket.on('close', ()=> {
console.log('the socket is closed')
});
});

server.listen(port, host, () => {
console.log('TCP server is running on port: ' + port);
});

Het client.js-bestand

const net = require('net');
const client = new net.Socket();
const port = 5000;
const host = '127.0.0.1';

client.connect(port, host, ()=> {
console.log("connected to server!");
client.write("Hi, I'm client " + client.address().address);
});

client.on('data', (data) => {
console.log(data.toString());
client.write("Goodbye");
client.end();
});

client.on('end', () => {
console.log('disconnected from server.');
});

U zult merken dat zowel de server- als de clientscripts een leesbare en schrijfbare stroom gebruiken om te communiceren (gegevens over te dragen en te ontvangen). Uiteraard draait de serverapplicatie als eerste en begint te luisteren naar verbindingen. Zodra u de client start, maakt deze verbinding met de server via het TCP-poortnummer.

Nadat een verbinding tot stand is gebracht, initieert de client de gegevensoverdracht door naar de server te schrijven met behulp van zijn SchrijfStream. De server registreert de gegevens die hij ontvangt op de terminal en schrijft vervolgens gegevens met behulp van zijn WriteStream. Ten slotte registreert de client de ontvangen gegevens, schrijft aanvullende gegevens en verbreekt vervolgens de verbinding met de server. De server blijft open zodat andere clients verbinding kunnen maken.

Een transformatiestroom maken en gebruiken

Transformatiestromen zijn duplexstromen waarbij de uitvoer gerelateerd is aan, maar verschilt van, de invoer. Node.js heeft twee soorten Transform-streams: zlib- en crypto-streams. Een zlib-stream kan een tekstbestand comprimeren en vervolgens decomprimeren na de bestandsoverdracht.

De compressFile.js-toepassing

const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');

(() => {
const source = createReadStream('myFile.txt');
const destination = createWriteStream('myFile.txt.gz');

source.pipe(zlib.createGzip()).pipe(destination);
})();

Dit eenvoudige script neemt het originele tekstbestand, comprimeert het en slaat het op in de huidige map. Dit is een eenvoudig proces dankzij de leesbare streams pijp() methode. Streampijplijnen maken het gebruik van buffers overbodig en leiden gegevens rechtstreeks van de ene stroom naar de andere.

Voordat de gegevens echter de beschrijfbare stroom in het script bereiken, is er een kleine omweg nodig via de createGzip() -methode van zlib. Deze methode comprimeert het bestand en retourneert een nieuw Gzip-object dat de schrijfstroom vervolgens ontvangt.

De decompressFile.js-toepassing

const zlib = require('zlib'); 
const { createReadStream, createWriteStream } = require('fs');
 
(() => {
const source = createReadStream('myFile.txt.gz');
const destination = createWriteStream('myFile2.txt');

source.pipe(zlib.createUnzip()).pipe(destination);
})();

Dit script hierboven neemt het gecomprimeerde bestand en decomprimeert het. Als je de nieuwe opent mijnBestand2.txt bestand, zul je zien dat het dezelfde gegevens bevat als het originele bestand:

Waarom zijn streams belangrijk?

Streams verbeteren de efficiëntie van gegevensoverdracht. Leesbare en beschrijfbare streams dienen als basis die communicatie tussen clients en servers mogelijk maakt, evenals de compressie en overdracht van grote bestanden.

Streams verbeteren ook de prestaties van programmeertalen. Zonder streams wordt het gegevensoverdrachtproces complexer, waardoor er meer handmatige invoer van ontwikkelaars nodig is, wat resulteert in meer fouten en prestatieproblemen.