Streams i Node.js kan være komplicerede, men det er værd at bruge tid på at forstå dem.

Nøgle takeaways

  • Streams i Node.js er et grundlæggende værktøj til databehandling og overførsel, hvilket gør dem ideelle til realtids- og begivenhedsdrevne applikationer.
  • For at oprette en skrivbar stream i Node.js kan du bruge fs-modulets createWriteStream()-funktion, som skriver data til en bestemt lokation.
  • Læsbar, skrivbar, dupleks og transformer er de fire typer streams i Node.js, hver med sit eget brugstilfælde og funktionalitet.

En strøm er et grundlæggende programmeringsværktøj, der beskæftiger sig med strømmen af ​​data. I sin kerne repræsenterer en strøm typisk den sekventielle overførsel af bytes fra et punkt til et andet. Node.js’ officielle dokumentation definerer en strøm som en abstrakt grænseflade, som du kan bruge til at arbejde med data.

Overførsel af data på en computer eller på tværs af et netværk er en ideel brug af en stream.

Streams i Node.js

Streams har spillet en væsentlig rolle i Node.js' succes. De er ideelle til databehandling i realtid og begivenhedsdrevne applikationer, to fremtrædende funktioner i Node.js runtime-miljøet.

instagram viewer

For at oprette en ny stream i Node.js skal du bruge stream-API'en, som udelukkende fungerer med Strings og Node.js bufferdata. Node.js har fire typer streams: skrivbar, læsbar, duplex og transformer.

Sådan opretter og bruger du en skrivbar stream

En skrivbar stream lader dig skrive eller sende data til et bestemt sted. fs (filsystem)-modulet har en WriteStream-klasse, som du kan bruge til at oprette en ny stream med fs.createWriteStream() fungere. Denne funktion accepterer stien til den fil, du vil skrive data til, samt en valgfri række af muligheder.

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

Denne kode importerer createWriteStream() funktion, som den anonyme pilefunktion bruger derefter til at oprette en strøm, der skriver data til myFile.txt. Den anonyme funktion indeholder en indre funktion kaldet skriveData() der skriver data.

Det createWriteStream() funktionen fungerer med en buffer til at skrive en samling af tal (0–9.999) til destinationsfilen. Men når du kører scriptet ovenfor, opretter det en fil i den samme mappe, der indeholder følgende data:

Den nuværende samling af numre ender på 2.915, men den skulle have inkluderet numre op til 9.999. Denne uoverensstemmelse opstår, fordi hver WriteStream bruger en buffer, der gemmer en fast mængde data ad gangen. For at lære, hvad denne standardværdi er, skal du konsultere højvandsmærke mulighed.

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

Tilføjelse af kodelinjen ovenfor til den anonyme funktion vil producere følgende output i terminalen:

Terminaludgangen viser, at standard højvandsmærke værdi (som kan tilpasses) er 16.384 bytes. Det betyder, at du kun kan gemme under 16.384 bytes data i denne buffer ad gangen. Så op til nummer 2.915 (plus alle kommaer og mellemrum) repræsenterer den maksimale mængde data, bufferen kan gemme på én gang.

Løsningen på bufferfejlen er at bruge en stream-hændelse. En strøm støder på forskellige begivenheder på forskellige stadier af dataoverførselsprocessen. Det dræne begivenhed er den passende mulighed for denne situation.

I den skriveData() funktionen ovenfor, kaldet til WriteStreams skrivning() funktion returnerer sand, hvis stykket af data (eller intern buffer) er under højvandsmærke værdi. Dette indikerer, at applikationen kan sende flere data til streamen. Men så snart skrive() funktionen returnerer false, sløjfen bryder, fordi du skal dræne bufferen.

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

Indsættelse af dræne hændelseskoden ovenfor i den anonyme funktion vil tømme WriteStreams buffer når den er ved kapacitet. Så minder den om skriveData() metode, så den kan fortsætte med at skrive data. Kørsel af det opdaterede program vil producere følgende output:

Du skal bemærke, at applikationen skulle dræne WriteStream buffer tre gange under udførelsen. Tekstfilen oplevede også nogle ændringer:

Sådan opretter og bruger du en læsbar stream

For at læse data skal du starte med at oprette en læsbar stream ved hjælp af fs.createReadStream() fungere.

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.");
});
})();

Scriptet ovenfor bruger createReadStream() metode til at få adgang til filen, som den tidligere kode oprettede: myFile.txt. Det createReadStream() funktion accepterer en filsti (som kan være i form af en streng, buffer eller URL) og flere valgfrie muligheder som argumenter.

I den anonyme funktion er der flere vigtige stream-begivenheder. Der er dog ingen tegn på dræne begivenhed. Dette skyldes, at en læsbar stream kun buffer data, når du kalder stream.push (chunk) funktion eller brug læselig begivenhed.

Det åben hændelsen udløses, når fs åbner den fil, du vil læse fra. Når du vedhæfter data hændelse til en implicit kontinuerlig strøm, får det strømmen til at gå over i flydende tilstand. Dette tillader data at passere igennem, så snart de bliver tilgængelige. Kørsel af applikationen ovenfor producerer følgende output:

Sådan opretter og bruger du en Duplex Stream

En duplex-stream implementerer både de skrivbare og læsbare stream-grænseflader, så du kan læse og skrive til en sådan stream. Et eksempel er en TCP-socket, der er afhængig af netmodulet til dets oprettelse.

En simpel måde at demonstrere egenskaberne ved en dupleksstrøm på er at oprette en TCP-server og -klient, der overfører data.

Server.js-filen

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

Filen client.js

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.');
});

Du vil bemærke, at både server- og klientscripts bruger en læsbar og skrivbar strøm til at kommunikere (overføre og modtage data). Serverapplikationen kører naturligvis først og begynder at lytte efter forbindelser. Så snart du starter klienten, opretter den forbindelse til serveren vha TCP-portnummeret.

Efter at have etableret en forbindelse, starter klienten dataoverførsel ved at skrive til serveren ved hjælp af dens WriteStream. Serveren logger de data, den modtager, til terminalen, og derefter skriver den data ved hjælp af sin WriteStream. Til sidst logger klienten de data, den modtager, skriver yderligere data og afbryder derefter forbindelsen til serveren. Serveren forbliver åben, så andre klienter kan oprette forbindelse.

Sådan opretter og bruger du en transformationsstrøm

Transformeringsstrømme er dupleksstrømme, hvor outputtet er relateret til, men forskelligt fra inputtet. Node.js har to typer Transform-streams: zlib- og krypto-streams. En zlib-stream kan komprimere en tekstfil og derefter dekomprimere den efter filoverførsel.

Programmet compressFile.js

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

Dette simple script tager den originale tekstfil, komprimerer den og gemmer den i den aktuelle mappe. Dette er en enkel proces takket være de læsbare strømme rør() metode. Stream pipelines fjerner brugen af ​​buffere og rørdata direkte fra en stream til en anden.

Men før dataene når den skrivbare strøm i scriptet, tager det en lille omvej via zlibs createGzip()-metode. Denne metode komprimerer filen og returnerer et nyt Gzip-objekt, som skrivestrømmen derefter modtager.

Applikationen decompressFile.js

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

Dette script ovenfor tager den komprimerede fil og dekomprimerer den. Hvis du åbner den nye myFile2.txt fil, vil du se, at den indeholder de samme data som den originale fil:

Hvorfor er streams vigtige?

Streams øger effektiviteten af ​​dataoverførsel. Læsbare og skrivbare streams tjener som fundamentet, der muliggør kommunikation mellem klienter og servere, samt komprimering og overførsel af store filer.

Streams forbedrer også programmeringssprogens ydeevne. Uden streams bliver dataoverførselsprocessen mere kompleks, hvilket kræver større manuel input fra udviklere og resulterer i flere fejl og ydeevneproblemer.