Strumienie w Node.js mogą być skomplikowane, ale warto poświęcić czas na ich zrozumienie.
Kluczowe dania na wynos
- Strumienie w Node.js są podstawowym narzędziem do przetwarzania i przesyłania danych, dzięki czemu idealnie nadają się do zastosowań w czasie rzeczywistym i sterowanych zdarzeniami.
- Aby utworzyć zapisywalny strumień w Node.js, możesz użyć funkcji createWriteStream() modułu fs, która zapisuje dane w określonej lokalizacji.
- Czytelny, zapisywalny, dupleks i transformacja to cztery typy strumieni w Node.js, każdy z własnym przypadkiem użycia i funkcjonalnością.
Strumień to podstawowe narzędzie programistyczne zajmujące się przepływem danych. W swojej istocie strumień zazwyczaj reprezentuje sekwencyjny transfer bajtów z jednego punktu do drugiego. Oficjalna dokumentacja Node.js definiuje strumień jako abstrakcyjny interfejs, którego można używać do pracy z danymi.
Przesyłanie danych na komputerze lub przez sieć jest idealnym wykorzystaniem strumienia.
Strumienie w Node.js
Strumienie odegrały kluczową rolę w sukcesie Node.js. Idealnie nadają się do przetwarzania danych w czasie rzeczywistym i aplikacji sterowanych zdarzeniami – dwóch najważniejszych cech środowiska wykonawczego Node.js.
Aby utworzyć nowy strumień w Node.js, musisz skorzystać z API strumieniowego, które działa wyłącznie z Stringami i Bufor danych Node.js. W Node.js dostępne są cztery typy strumieni: zapisywalne, czytelne, dupleksowe i przekształcane.
Jak utworzyć i używać zapisywalnego strumienia
Zapisywalny strumień umożliwia zapisywanie lub wysyłanie danych do określonej lokalizacji. Moduł fs (system plików) zawiera klasę WriteStream, której można użyć do utworzenia nowego strumienia z rozszerzeniem fs.createWriteStream() funkcjonować. Ta funkcja akceptuje ścieżkę do pliku, do którego chcesz zapisać dane, a także opcjonalną tablicę opcji.
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();
})();
Ten kod importuje plik utwórzWriteStream() funkcja, która anonimowa funkcja strzałkowa następnie używa do utworzenia strumienia, który zapisuje dane do myFile.txt. Funkcja anonimowa zawiera funkcję wewnętrzną o nazwie napiszDane() który zapisuje dane.
The utwórzWriteStream() funkcja współpracuje z buforem, aby zapisać zbiór liczb (0–9 999) do pliku docelowego. Jednak po uruchomieniu powyższego skryptu tworzy on w tym samym katalogu plik zawierający następujące dane:
Obecny zbiór liczb kończy się na 2915, ale powinien zawierać liczby aż do 9999. Ta rozbieżność występuje, ponieważ każdy WriteStream używa bufora, który przechowuje jednocześnie stałą ilość danych. Aby dowiedzieć się, jaka jest ta wartość domyślna, musisz zapoznać się z znak wysokiej wody opcja.
console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");
Dodanie powyższego wiersza kodu do funkcji anonimowej spowoduje wyświetlenie w terminalu następujących wyników:
Dane wyjściowe terminala pokazują, że wartość domyślna znak wysokiej wody wartość (którą można dostosować) to 16 384 bajtów. Oznacza to, że w tym buforze można przechowywać jednocześnie maksymalnie 16 384 bajtów danych. Zatem liczba do 2915 (plus wszystkie przecinki i spacje) reprezentuje maksymalną ilość danych, które bufor może jednocześnie przechowywać.
Rozwiązaniem błędu bufora jest użycie zdarzenia strumieniowego. Strumień napotyka różne zdarzenia na różnych etapach procesu przesyłania danych. The odpływ event jest odpowiednią opcją w tej sytuacji.
w napiszDane() powyżej, wywołanie funkcji WriteStream pisze() funkcja zwraca wartość true, jeśli fragment danych (lub bufor wewnętrzny) znajduje się poniżej znak wysokiej wody wartość. Oznacza to, że aplikacja może wysłać do strumienia więcej danych. Jednakże, gdy tylko pisać() funkcja zwraca wartość false, pętla zostaje przerwana, ponieważ trzeba opróżnić bufor.
myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});
Wkładanie odpływ powyższy kod zdarzenia do funkcji anonimowej spowoduje opróżnienie pliku Bufor WriteStream kiedy jest w pełni sprawny. Następnie przypomina napiszDane() metodę, aby mógł kontynuować zapisywanie danych. Uruchomienie zaktualizowanej aplikacji spowoduje wyświetlenie następujących wyników:
Należy pamiętać, że aplikacja musiała opróżnić Bufor WriteStream trzykrotnie w trakcie jego realizacji. Plik tekstowy również uległ pewnym zmianom:
Jak utworzyć i używać czytelnego strumienia
Aby odczytać dane, zacznij od utworzenia czytelnego strumienia za pomocą metody fs.createReadStream() funkcjonować.
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.");
});
})();
Powyższy skrypt używa utwórzReadStream() metoda dostępu do pliku utworzonego przez poprzedni kod: myFile.txt. The utwórzReadStream() funkcja akceptuje ścieżkę pliku (która może mieć postać ciągu, bufora lub adresu URL) i kilka opcjonalnych opcji jako argumentów.
W funkcji anonimowej istnieje kilka ważnych zdarzeń strumieniowych. Nie ma jednak śladu odpływ wydarzenie. Dzieje się tak, ponieważ czytelny strumień buforuje dane tylko wtedy, gdy wywołasz metodę strumień.push (fragment) funkcję lub użyj czytelny wydarzenie.
The otwarty zdarzenie jest uruchamiane, gdy fs otwiera plik, z którego chcesz przeczytać. Kiedy dołączysz dane zdarzenie do niejawnie ciągłego strumienia, powoduje to przejście strumienia do trybu przepływu. Dzięki temu dane mogą zostać przesłane natychmiast po ich udostępnieniu. Uruchomienie powyższej aplikacji daje następujące dane wyjściowe:
Jak utworzyć i używać strumienia dupleksowego
Strumień dupleksowy implementuje zarówno zapisywalny, jak i czytelny interfejs strumienia, dzięki czemu można czytać i zapisywać w takim strumieniu. Jednym z przykładów jest gniazdo TCP, którego utworzenie opiera się na module sieciowym.
Prostym sposobem zademonstrowania właściwości strumienia dupleksowego jest utworzenie serwera i klienta TCP, który przesyła dane.
Plik serwer.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);
});
Plik klient.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.');
});
Zauważysz, że zarówno skrypty serwera, jak i klienta używają strumienia możliwego do odczytu i zapisu do komunikacji (przesyłania i odbierania danych). Oczywiście aplikacja serwera działa jako pierwsza i zaczyna nasłuchiwać połączeń. Gdy tylko uruchomisz klienta, łączy się on z serwerem za pomocą numer portu TCP.
Po nawiązaniu połączenia klient inicjuje przesyłanie danych poprzez zapis do serwera za jego pomocą Zapis strumienia. Serwer rejestruje otrzymane dane na terminalu, a następnie zapisuje dane za pomocą WriteStream. Na koniec klient loguje otrzymane dane, zapisuje dodatkowe dane, a następnie rozłącza się z serwerem. Serwer pozostaje otwarty, aby inni klienci mogli się połączyć.
Jak utworzyć i używać strumienia transformacji
Strumienie transformacji to strumienie dupleksowe, w których dane wyjściowe są powiązane z danymi wejściowymi, ale różnią się od nich. Node.js ma dwa typy strumieni Transform: strumienie zlib i crypto. Strumień zlib może skompresować plik tekstowy, a następnie zdekompresować go po przesłaniu pliku.
Aplikacja 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);
})();
Ten prosty skrypt pobiera oryginalny plik tekstowy, kompresuje go i zapisuje w bieżącym katalogu. Jest to prosty proces dzięki czytelnemu strumieniowi rura() metoda. Potoki strumieniowe eliminują użycie buforów i przesyłają dane bezpośrednio z jednego strumienia do drugiego.
Zanim jednak dane dotrą do zapisywalnego strumienia w skrypcie, należy trochę ominąć metodę createGzip() biblioteki zlib. Ta metoda kompresuje plik i zwraca nowy obiekt Gzip, który następnie odbiera strumień zapisu.
Aplikacja 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);
})();
Powyższy skrypt pobiera skompresowany plik i dekompresuje go. Jeśli otworzysz nowe mójPlik2.txt plik, zobaczysz, że zawiera on te same dane, co plik oryginalny:
Dlaczego strumienie są ważne?
Strumienie zwiększają efektywność przesyłania danych. Czytelne i zapisywalne strumienie stanowią podstawę umożliwiającą komunikację pomiędzy klientami i serwerami, a także kompresję i przesyłanie dużych plików.
Strumienie poprawiają także wydajność języków programowania. Bez strumieni proces przesyłania danych staje się bardziej złożony, wymaga większego ręcznego wkładu programistów i skutkuje większą liczbą błędów i problemów z wydajnością.