Streamy v Node.js mohou být složité, ale vyplatí se jim věnovat čas, abyste jim porozuměli.
Klíčové věci
- Streamy v Node.js jsou základním nástrojem pro zpracování a přenos dat, díky čemuž jsou ideální pro aplikace řízené v reálném čase a událostmi.
- Chcete-li vytvořit zapisovatelný stream v Node.js, můžete použít funkci createWriteStream() modulu fs, která zapisuje data do konkrétního umístění.
- Čitelné, zapisovatelné, duplexní a transformovatelné jsou čtyři typy streamů v Node.js, z nichž každý má svůj vlastní případ použití a funkce.
Proud je základní programovací nástroj, který se zabývá tokem dat. Ve svém jádru tok typicky představuje sekvenční přenos bajtů z jednoho bodu do druhého. Oficiální dokumentace Node.js definuje stream jako abstraktní rozhraní, které můžete použít k práci s daty.
Ideálním využitím streamu je přenos dat v počítači nebo po síti.
Streamy v Node.js
Streamy hrály zásadní roli v úspěchu Node.js. Jsou ideální pro zpracování dat v reálném čase a aplikace řízené událostmi, což jsou dvě významné funkce běhového prostředí Node.js.
Chcete-li vytvořit nový stream v Node.js, budete muset použít rozhraní API pro streamování, které funguje výhradně s řetězci a Data vyrovnávací paměti Node.js. Node.js má čtyři typy streamů: zapisovatelný, čitelný, duplexní a transformovaný.
Jak vytvořit a používat zapisovatelný stream
Zapisovatelný datový proud vám umožňuje zapisovat nebo odesílat data do konkrétního umístění. Modul fs (systém souborů) má třídu WriteStream, kterou můžete použít k vytvoření nového streamu pomocí fs.createWriteStream() funkce. Tato funkce přijímá cestu k souboru, do kterého chcete zapisovat data, a také volitelné pole voleb.
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() funkce, která funkce anonymní šipky pak použije k vytvoření streamu, který zapíše data do souboru myFile.txt. Anonymní funkce obsahuje vnitřní funkci tzv writeData() který zapisuje data.
The createWriteStream() funkce pracuje s vyrovnávací pamětí pro zapsání kolekce čísel (0–9 999) do cílového souboru. Když však spustíte výše uvedený skript, vytvoří se ve stejném adresáři soubor, který obsahuje následující data:
Aktuální sbírka čísel končí na 2 915, ale měla obsahovat čísla do 9 999. K této nesrovnalosti dochází, protože každý WriteStream používá vyrovnávací paměť, která ukládá pevné množství dat najednou. Chcete-li zjistit, jaká je tato výchozí hodnota, budete se muset podívat na horní vodoznak volba.
console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");
Přidáním výše uvedeného řádku kódu do anonymní funkce vytvoříte v terminálu následující výstup:
Výstup terminálu ukazuje, že výchozí horní vodoznak hodnota (která je přizpůsobitelná) je 16 384 bajtů. To znamená, že do této vyrovnávací paměti můžete najednou uložit pouze méně než 16 384 bajtů dat. Takže až číslo 2 915 (plus všechny čárky a mezery) představuje maximální množství dat, které může vyrovnávací paměť najednou uložit.
Řešením chyby vyrovnávací paměti je použití události streamu. Proud se setkává s různými událostmi v různých fázích procesu přenosu dat. The vypustit event je pro tuto situaci vhodnou volbou.
V writeData() funkce výše, volání na WriteStream's write() funkce vrátí hodnotu true, pokud je část dat (nebo vnitřní vyrovnávací paměť) pod hodnotou horní vodoznak hodnota. To znamená, že aplikace může odeslat více dat do streamu. Nicméně, jakmile napsat() funkce vrací false smyčka se přeruší, protože potřebujete vyprázdnit vyrovnávací paměť.
myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});
Vkládání vypustit kód události výše do anonymní funkce vyprázdní Vyrovnávací paměť WriteStreamu když je kapacita. Poté připomíná writeData() metoda, takže může pokračovat v zápisu dat. Spuštění aktualizované aplikace vytvoří následující výstup:
Měli byste si uvědomit, že aplikace musela vypustit WriteStream buffer třikrát během jeho realizace. Textový soubor také doznal některých změn:
Jak vytvořit a používat čitelný stream
Chcete-li číst data, začněte vytvořením čitelného streamu pomocí fs.createReadStream() funkce.
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.");
});
})();
Výše uvedený skript používá createReadStream() metoda pro přístup k souboru, který vytvořil předchozí kód: myFile.txt. The createReadStream() funkce přijímá cestu k souboru (která může být ve formě řetězce, vyrovnávací paměti nebo URL) a několik volitelných možností jako argumenty.
V anonymní funkci je několik důležitých událostí streamu. Neexistuje však žádná známka vypustit událost. Je to proto, že čitelný proud ukládá data do vyrovnávací paměti pouze tehdy, když voláte stream.push (kus) funkci nebo použijte čitelný událost.
The OTEVŘENO událost se spustí, když fs otevře soubor, ze kterého chcete číst. Když připojíte data událost na implicitně kontinuální proud, způsobí přechod proudu do plynulého režimu. To umožňuje průchod dat, jakmile budou k dispozici. Spuštěním výše uvedené aplikace vznikne následující výstup:
Jak vytvořit a používat duplexní stream
Duplexní tok implementuje rozhraní toku pro zápis i čtení, takže můžete do takového toku číst a zapisovat. Jedním příkladem je TCP socket, který se při vytváření spoléhá na síťový modul.
Jednoduchým způsobem, jak demonstrovat vlastnosti duplexního toku, je vytvořit TCP server a klienta, který přenáší data.
Soubor 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);
});
Soubor 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 serveru i klienta používají ke komunikaci (přenos a příjem dat) čitelný a zapisovatelný datový proud. Serverová aplikace se přirozeně spustí jako první a začne naslouchat připojení. Jakmile spustíte klienta, připojí se k serveru pomocí číslo TCP portu.
Po navázání spojení klient zahájí přenos dat zápisem na server pomocí jeho WriteStream. Server zaznamenává data, která obdrží, do terminálu, poté zapisuje data pomocí svého WriteStream. Nakonec klient zaznamená data, která obdrží, zapíše další data a poté se odpojí od serveru. Server zůstává otevřený pro připojení ostatních klientů.
Jak vytvořit a používat transformační proud
Transformační toky jsou duplexní toky, ve kterých výstup souvisí se vstupem, ale liší se od něj. Node.js má dva typy toků Transform: zlib a crypto streamy. Proud zlib může komprimovat textový soubor a poté jej po přenosu souboru dekomprimovat.
Aplikace 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ý soubor, zkomprimuje jej a uloží do aktuálního adresáře. Jedná se o jednoduchý proces díky čitelnému streamu trubka() metoda. Proudové kanály odstraňují použití vyrovnávacích pamětí a dat kanálu přímo z jednoho proudu do druhého.
Než se však data dostanou do zapisovatelného streamu ve skriptu, je třeba trochu odbočit pomocí metody createGzip() zlib. Tato metoda zkomprimuje soubor a vrátí nový objekt Gzip, který pak přijme stream pro zápis.
Aplikace 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 výše vezme komprimovaný soubor a dekomprimuje jej. Pokud otevřete nový myFile2.txt soubor, uvidíte, že obsahuje stejná data jako původní soubor:
Proč jsou streamy důležité?
Streamy zvyšují efektivitu přenosu dat. Čitelné a zapisovatelné streamy slouží jako základ, který umožňuje komunikaci mezi klienty a servery, stejně jako kompresi a přenos velkých souborů.
Proudy také zlepšují výkon programovacích jazyků. Bez streamů se proces přenosu dat stává složitějším, vyžaduje větší ruční vstup od vývojářů a vede k většímu počtu chyb a problémů s výkonem.