Streamy v Node.js môžu byť komplikované, no oplatí sa im venovať čas, aby ste im porozumeli.

Kľúčové poznatky

  • Prúdy v Node.js sú základným nástrojom na spracovanie a prenos údajov, vďaka čomu sú ideálne pre aplikácie riadené udalosťami a v reálnom čase.
  • Na vytvorenie zapisovateľného streamu v Node.js môžete použiť funkciu createWriteStream() modulu fs, ktorá zapisuje údaje na konkrétne miesto.
  • Čitateľný, zapisovateľný, duplexný a transformovateľný sú štyri typy streamov v Node.js, z ktorých každý má svoj vlastný prípad použitia a funkčnosť.

Prúd je základným programovacím nástrojom, ktorý sa zaoberá tokom údajov. Vo svojom jadre tok zvyčajne predstavuje postupný prenos bajtov z jedného bodu do druhého. Oficiálna dokumentácia Node.js definuje stream ako abstraktné rozhranie, ktoré môžete použiť na prácu s údajmi.

Prenos dát na počítači alebo cez sieť je ideálnym využitím streamu.

Streamy v Node.js

Streamy zohrali zásadnú úlohu v úspechu Node.js. Sú ideálne pre spracovanie údajov v reálnom čase a aplikácie riadené udalosťami, čo sú dve prominentné funkcie runtime prostredia Node.js.

instagram viewer

Ak chcete vytvoriť nový stream v Node.js, budete musieť použiť rozhranie API streamu, ktoré funguje výlučne s reťazcami a Údaje vyrovnávacej pamäte Node.js. Node.js má štyri typy tokov: zapisovateľný, čitateľný, duplexný a transformovateľný.

Ako vytvoriť a používať zapisovateľný stream

Zapisovateľný stream vám umožňuje zapisovať alebo odosielať údaje na konkrétne miesto. Modul fs (systém súborov) má triedu WriteStream, ktorú môžete použiť na vytvorenie nového streamu s fs.createWriteStream() funkciu. Táto funkcia akceptuje cestu k súboru, do ktorého chcete zapisovať údaje, ako aj voliteľné pole možností.

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

Tento kód importuje createWriteStream() funkciu, ktorá funkcia anonymnej šípky potom použije na vytvorenie streamu, ktorý zapíše údaje do súboru myFile.txt. Anonymná funkcia obsahuje vnútornú funkciu tzv writeData() ktorý zapisuje dáta.

The createWriteStream() funkcia pracuje s vyrovnávacou pamäťou na zapísanie zbierky čísel (0–9 999) do cieľového súboru. Keď však spustíte skript uvedený vyššie, vytvorí sa súbor v rovnakom adresári, ktorý obsahuje nasledujúce údaje:

Aktuálny zber čísel končí na čísle 2 915, no mal obsahovať čísla do 9 999. Táto nezrovnalosť sa vyskytuje, pretože každý WriteStream používa vyrovnávaciu pamäť, ktorá ukladá pevné množstvo údajov naraz. Ak chcete zistiť, aká je táto predvolená hodnota, musíte si pozrieť horná značka vody možnosť.

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

Pridaním vyššie uvedeného riadku kódu do anonymnej funkcie sa v termináli vytvorí nasledujúci výstup:

Výstup terminálu zobrazuje predvolenú hodnotu horná značka vody hodnota (ktorá je prispôsobiteľná) je 16 384 bajtov. To znamená, že do tejto vyrovnávacej pamäte môžete naraz uložiť iba menej ako 16 384 bajtov údajov. Takže až do čísla 2 915 (plus všetky čiarky a medzery) predstavuje maximálne množstvo údajov, ktoré môže vyrovnávacia pamäť uložiť naraz.

Riešením chyby vyrovnávacej pamäte je použitie udalosti streamu. Prúd sa stretáva s rôznymi udalosťami v rôznych fázach procesu prenosu údajov. The vypustiť udalosť je pre túto situáciu vhodnou možnosťou.

V writeData() funkcie vyššie, volanie na WriteStream's write() funkcia vráti hodnotu true, ak je časť údajov (alebo vnútorná vyrovnávacia pamäť) pod hodnotou horná značka vody hodnotu. To znamená, že aplikácia môže odosielať viac údajov do streamu. Avšak, akonáhle písať () funkcia vráti false, cyklus sa preruší, pretože potrebujete vyprázdniť vyrovnávaciu pamäť.

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

Vkladanie vypustiť kód udalosti vyššie do anonymnej funkcie vyprázdni Vyrovnávacia pamäť WriteStreamu keď je naplnená kapacita. Potom pripomína writeData() metóda, takže môže pokračovať v zapisovaní údajov. Spustenie aktualizovanej aplikácie vytvorí nasledujúci výstup:

Mali by ste si uvedomiť, že aplikácia musela vypustiť WriteStream vyrovnávacia pamäť trikrát počas jeho vykonávania. Textový súbor tiež zaznamenal niekoľko zmien:

Ako vytvoriť a používať čitateľný stream

Ak chcete čítať údaje, začnite vytvorením čitateľného toku pomocou fs.createReadStream() funkciu.

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

Vyššie uvedený skript používa createReadStream() spôsob prístupu k súboru, ktorý vytvoril predchádzajúci kód: myFile.txt. The createReadStream() funkcia akceptuje cestu k súboru (ktorá môže byť vo forme reťazca, vyrovnávacej pamäte alebo adresy URL) a niekoľko voliteľných možností ako argumenty.

V anonymnej funkcii je niekoľko dôležitých streamových udalostí. Neexistuje však žiadny náznak vypustiť udalosť. Je to preto, že čitateľný tok ukladá údaje do vyrovnávacej pamäte iba vtedy, keď zavoláte stream.push (kus) funkciu alebo použite čitateľný udalosť.

The OTVORENÉ udalosť sa spustí, keď fs otvorí súbor, z ktorého chcete čítať. Keď pripojíte údajov udalosť na implicitne kontinuálny prúd, spôsobí prechod prúdu do plynulého režimu. To umožňuje, aby údaje prešli hneď, ako budú k dispozícii. Spustenie vyššie uvedenej aplikácie vytvorí nasledujúci výstup:

Ako vytvoriť a používať duplexný tok

Duplexný tok implementuje zapisovateľné aj čitateľné rozhrania toku, takže môžete čítať a zapisovať do takéhoto toku. Jedným príkladom je TCP socket, ktorý sa pri vytváraní spolieha na sieťový modul.

Jednoduchý spôsob, ako demonštrovať vlastnosti duplexného toku, je vytvoriť TCP server a klienta, ktorý prenáša dáta.

Súbor server.js

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

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

Všimnete si, že skripty servera aj klienta používajú na komunikáciu (prenos a príjem údajov) čitateľný a zapisovateľný tok. Prirodzene, serverová aplikácia sa spustí ako prvá a začne počúvať pripojenia. Hneď ako spustíte klienta, pripojí sa k serveru pomocou číslo TCP portu.

Po nadviazaní spojenia klient iniciuje prenos dát zápisom na server pomocou svojho WriteStream. Server zaznamená údaje, ktoré dostane, do terminálu, potom zapíše údaje pomocou svojho WriteStream. Nakoniec klient zaznamená údaje, ktoré dostane, zapíše ďalšie údaje a potom sa odpojí od servera. Server zostáva otvorený pre ostatných klientov, aby sa mohli pripojiť.

Ako vytvoriť a použiť transformačný prúd

Transformačné toky sú duplexné toky, v ktorých výstup súvisí so vstupom, ale je od neho odlišný. Node.js má dva typy transformačných streamov: zlib a krypto streamy. Prúd zlib môže komprimovať textový súbor a potom ho po prenose súboru dekomprimovať.

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

Tento jednoduchý skript vezme pôvodný textový súbor, skomprimuje ho a uloží do aktuálneho adresára. Ide o jednoduchý proces vďaka čitateľnému streamu rúra() metóda. Potrubia tokov odstraňujú používanie vyrovnávacích pamätí a údajov potrubia priamo z jedného toku do druhého.

Kým sa však údaje dostanú do zapisovateľného streamu v skripte, je potrebné urobiť malú odbočku pomocou metódy createGzip() zlib. Táto metóda skomprimuje súbor a vráti nový objekt Gzip, ktorý potom prijme prúd zápisu.

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

Tento skript uvedený vyššie vezme komprimovaný súbor a dekomprimuje ho. Ak otvoríte nové myFile2.txt súbor, uvidíte, že obsahuje rovnaké údaje ako pôvodný súbor:

Prečo sú streamy dôležité?

Streamy zvyšujú efektivitu prenosu údajov. Čitateľné a zapisovateľné prúdy slúžia ako základ, ktorý umožňuje komunikáciu medzi klientmi a servermi, ako aj kompresiu a prenos veľkých súborov.

Prúdy tiež zlepšujú výkon programovacích jazykov. Bez streamov sa proces prenosu údajov stáva zložitejším, čo si vyžaduje väčší manuálny vstup od vývojárov a výsledkom je viac chýb a problémov s výkonom.