w Praktyce
10 godz. 44 min · Node.js · Full-stack i Programowanie
Piotr PalarzWeb DeveloperW kursie tym poznasz platformę Node.js od podstaw, aż po bardziej zaawansowane koncepcje. Zaczniemy od omówienia czym jest Node, a także jakie może być zastosowanie tej technologii. Następnie przejdziemy przez proces instalacji i napisanie swojego pierwszego skryptu. Już na samym początku dokładnie omówimy tworzenie własnych modułów, gdyż jest to wiedza niezbędna, by dobrze zrozumieć funkcjonowanie Node. Następnie dowiesz się czym są zdarzenia, jak działa model “publish / subscribe”, a także czym jest “event-driven development”. Chwilę później omówimy pracę z buforami, stream’ami, a także ze standardowym wejściem i wyjściem. Ta wiedza pozwoli Ci zrozumieć jak tworzone aplikacje mogą otrzymywać od użytkownika dane oraz jak je wyświetlają.
Niezwykle ważnym rozdziałem będzie ten, poświęcony pracy z systemem plików. Szczególnie dla dewelopera, pracującego dotychczas z językiem JavaScript wyłącznie w środowisku przeglądarki internetowej. Node pozwala bowiem tworzyć nowe pliki, zapisywać w nich dane oraz odczytywać i usuwać pliki i katalogi. Przy wszystkich tych operacjach, niezwykle ważna jest jednak asynchroniczna natura Node. Dowiesz się jak działa, a także jakie są jej zalety. Na tym etapie nie zabraknie oczywiście praktycznego projektu, którym będzie aplikacja typu CLI (Command Line Interface). Pozwoli nam ona grupowo zmienić nazwy wielu plików, według podanego wzorca.
W kolejnym rozdziale poruszymy fundamentalną dla Node kwestię, mianowicie tworzenie serwerów sieciowych. Zaczniemy od najniższego poziomu, tworząc prosty serwer bazujący na protokole TCP/IP. Wiedza w jaki sposób Node radzi sobie z takim zadaniem, pozwoli później lepiej zrozumieć wyższe warstwy, takie jak np. HTTP czy WebSockets. Chwilę później stworzymy swój pierwszy serwer HTTP, do którego będzie można wysyłać zapytania wprost z przeglądarki internetowej. Omówimy również jak w Node wykorzystać bezpieczne szyfrowanie HTTPS.Na tym etapie będziesz już miał solidną wiedzę jak pracować z Node, jednak zanim przejdziemy dalej, zatrzymamy się, by omówić jak dokładnie działa Node.js, a także jak ta platforma została zbudowana. Dwie dość obszerne lekcje, będą być może jednymi z najważniejszych w tym kursie. Dowiesz się w nich czym jest, a także jak działa jednowątkowa pętla zdarzeń, czym jest proces i wątek, a także zobaczysz kod źródłowy Node.js i wszystkie bloki budulcowe, z jakich Node się składa. To naszym zdaniem niezwykle ważny temat, często jednak pomijany w wielu materiałach. Dzięki dogłębnemu zrozumieniu jednowątkowej natury języka JavaScript i asynchroniczności Node, będziesz mógł tworzyć dużo lepsze aplikacje.
Node.js posiada kilkanaście wbudowanych modułów, z których w dowolnej chwili można skorzystać. Wszystkie jednak dają nam dużą, niskopoziomową kontrolę, ale czasami sporo kodu należy napisać, by zrealizować jakieś zadanie. Jest to jednak świadomy wybór twórców Node, gdyż całą resztę świetnych rozwiązań, dopisuje nieustannie ogólnoświatowa społeczność deweloperów. Zdecydowana większość z modułów ląduje w rejestrze npm. Jest to genialny package manager, który pozwoli nam wyszukiwać, pobierać i aktualizować niezliczoną ilość modułów. Dowiesz się zatem jak korzystać z npm, a chwilę później zaczniemy wykorzystywać zgromadzone tam moduły w dalszej pracy.Jednym z najpopularniejszych modułów jest express.js, który pozwala w bardzo prosty sposób tworzyć serwery HTTP. Grzechem byłoby zatem z niego nie skorzystać. Dowiesz się wszystkiego co niezbędne, by zacząć tworzyć własne aplikacje bazujące na protokole HTTP z użyciem express.js, a także kilku dodatków do tego systemu. Zobaczysz jak routować zapytania, jak korzystać z middleware czy serwować statyczne pliki. Następnie omówimy współpracę z bazą danych MongoDB i z jej wykorzystaniem stworzymy API typu REST. Stworzona aplikacja pozwoli odczytywać dane z bazy i przesyłać je do klienta w formacie JSON, a także dodawać nowe dane, aktualizować i usuwać je. Wszystko to zgodnie z dobrymi praktykami pracy z REST.
Chwilę później czeka Cię kolejny praktyczny projekt - skracacz linków. Stworzymy wspólnie aplikację, która pozwoli skrócić dowolny adres URL do krótkiej formy. Oczywiście będzie działać również w drugą stronę! Kiedy przejdziemy pod skrócony adres, przekieruje nas do odpowiedniej witryny. Node.js to platforma wręcz stworzona do tworzenia aplikacji typu real-time, dlatego w kolejnym rodziale podejmiemy temat technologii WebSockets. Dowiesz się jakie są jej zalety, a także jak pracować z nią po stronie serwera i klienta. Tutaj czeka nas kolejny praktyczny, niezwykle ciekawy projekt. Wykonamy bowiem czat grupowy. Aplikacja ta pozwoli dowolnej ilości użytkowników podłączyć się do czatu podając swój nick, a następnie wysyłać widoczne dla wszystkich wiadomości. Zaimplementujemy nawet takie rozwiązania jak wyświetlanie statusów o dołączeniu kogoś do czatu, a także o jego opuszczeniu.W przedostatnim rodziale tego kursu podejmiemy tematykę dobrych praktyk pracy z Node.js. Na początku omówimy najważniejsze konstrukcje nowej specyfikacji EcmaScript 2015, które są znakomicie wspierane w Node. Chwilę później omówimy jak korzystanie z Promises uprości, a także ulepszy nasz kod. Wśród dobrych praktyk nie zabraknie również informacji o debugowaniu aplikacji. Zobaczysz sprawdzone sposoby, by znaleźć błędy lub lepiej, krok po kroku, zrozumieć jak działa napisany wcześniej kod. Dowiesz się również jak pracować z błędami, by Twoje aplikacje działały w sposób przewidywalny.
Ostatni rozdział w całości został poświęcony temu, co zwykle jest pomijane w innych materiałach, mianowicie wdrażaniu aplikacji do produkcji. Przez cały ten kurs pracować będziemy lokalnie, lecz kiedy aplikacje są już gotowe, wypadałoby udostępnić je światu. Wdrażanie aplikacji napisanych z użyciem Node.js nie jest jednak tak oczywiste, jak np. wgrywanie WordPress’a u wybranego hostingodawcy. Ty będziesz miał jednak możliwość zobaczyć, jak wdrożyć napisany przez nas grupowy czat na platformie Heroku. Jest to bardzo popularny serwis działający jako PaaS (Platform as a Service). Za darmo będziesz mógł w ciągu kilku chwil uruchomić swoją aplikację.Zobaczysz o co należy zadbać, by wszystko poszło gładko. Serwisy typu PaaS dbają o bardzo wiele aspektów wdrażania i serwowania naszych aplikacji, takich jak bezpieczeństwo oraz nieustanną dostępność. Mają jednak pewne ograniczenia. Z tego powodu, dowiesz się również jak wdrożyć swoją aplikację na serwerze wirtualnym VPS z systemem Ubuntu Server. Takie rozwiązanie daje nam całkowitą kontrolę, ale także obarczone jest większą odpowiedzialnością. Zaczniemy od instalacji na serwerze platformy Node.js, systemu kontroli wersji GIT, a także innych niezbędnych modułów
Następnie zobaczysz jak skonfigurować swoje lokalne środowisko tak, by za pomocą GIT’a wysyłać kod do zdalnego serwera, a potem jednym poleceniem wdrażać go do produkcji. Dowiesz się również jak jednocześnie serwować wersję produkcyjną oraz developmencką. W rodziale tym poruszymy również inne kwestie, takie jak procesy potomne czy tworzenie klastrów. Dzięki tej wiedzy, będziesz mógł maksymalnie wykorzystać dostępne zasoby serwera. Na sam koniec tego kursu rzucimy okiem również na inne, nie przedstawione wcześniej zastosowania Node, a także nakreślimy dalszą drogę nauki w tym zakresie.
Kurs ten jest dla wszystkich osób, które dobrze czują się w technologiach frontendowych, tj. HTML, CSS i JavaScript, a teraz chcą rozpocząć swoją przygodę z back-endem. Im zatem lepiej znasz język JavaScript, tym więcej wyciśniesz z Node, natomiast nie jest wymagana bardzo zaawansowana wiedza z zakresu tego języka.
Im dalej poznajemy Node, tym ciekawiej. I w tej lekcji pokażę ci, w jaki sposób działają streamy i do czego
służą.
I tylko ważna informacja na początek. Stream jest takim abstrakcyjnym interfejsem, a to oznacza, że nie
jest on używany bezpośrednio, czyli nie będziemy tworzyć nowych obiektów typu stream itd.
Ale jest to pewnego rodzaju klasa, która jest w Node.js napisana na warstwie JavaScript, ale o tym będzie
później.
I bardzo wiele modułów w Node korzysta właśnie ze streamu. Mam na myśli wbudowane moduły.
Dlatego warto wiedzieć, gdzie streamy się pojawiają i jak z nich korzystać.
Głównym zadaniem streamu w połączeniu z bufferem, który widziałeś w lekcji poprzedniej, jest ograniczenie
zużycia pamięci. Czyli tak jak wspominałem w poprzedniej lekcji, możemy np. odczytywać dane z jakiegoś
pliku po kawałeczku. Takie kawałki nazywane są chunk. I następnie będziemy je przekazywać dalej i ponownie
odczytywać, aż odczytamy wszystkie takie dane.
Ważne jest jeszcze to, że mamy kilka rodzajów streamów w Node.js.
One mogą być odczytywalne, czyli readable. Z takiego streamu możemy wyłącznie dane odczytywać. Może być
zapisywalny, czyli writable.
Możemy do niego zapisywać.
Może być taki i taki, czyli i odczytywalny, i zapisywalny. Wtedy nazywa się duplex. Albo może być to stream transformacyjny,
czyli transform. To jest taki, gdzie pewnego rodzaju dane wchodzą do niego, są zmieniane i wychodzą właśnie w
takiej postaci już przetransformowanej. Co jest jeszcze super ważne? Pokazywałem ci klasę EventEmitter
nie bez powodu. Wiesz, jak wykorzystać ją dla własnych celów, ale dodatkowo wiele modułów w Node z niej dziedziczy.
I właśnie stream jest jednym z takich modułów, dlatego tutaj będziemy mogli spokojnie korzystać z wszystkiego
tego, co już znasz,
jeżeli chodzi o klasę EventEmitter.
Ok, przejdźmy zatem do rzeczy.
Mamy tutaj w katalogu plik, który nazywa się text.txt, i zajmuje on około 140 kb. Mamy tutaj
tekst lorem ipsum.
Będę chciał ten plik odczytywać.
Natomiast na razie jeszcze nie poznałeś modułu file system, który będzie w kolejnym rozdziale omawiany,
ale musimy już tutaj na początku z niego skorzystać
na prostym przykładzie. Dlatego zaimportujemy go sobie za pomocą
require("fs").
Oczywiście musimy utworzyć sobie stałą w ten sposób i ten moduł ma bardzo wiele metod do pracy z systemem
plików.
Jedną z takich metod jest createReadStream, czyli możemy utworzyć taki odczytywalny stream.
I ta metoda właśnie dziedziczy sobie
pod spodem z tego abstrakcyjnego interfejsu, jakim jest stream.
Dlatego będę chciał to w tym momencie zrobić. Nazwiemy sobie zmienną stream i następnie fs.createReadStream.
Tutaj musimy podać ścieżkę do pliku. Jest on w tym samym katalogu, więc wiesz, że podajemy to w ten sposób:
text.txt.
I samo to, że wywołaliśmy w taki sposób createReadStream
jeszcze tak naprawdę nic nie zrobi, nie odczytamy żadnych danych.
Ale teraz abyśmy te dane mogli odczytywać, do tego streamu możemy przypisywać zdarzenia, bo dziedziczy on
z EventEmittera.
Jednym z takich zdarzeń jest data.
Jeżeli je przypiszemy, to musimy tutaj podać funkcję, która będzie przyjmować taki jeden kawałek danych odczytany,
będzie to chunk.
W ten oto sposób będziemy mogli sobie coś z tymi danymi zrobić.
Super ważne jest to, że ten chunk to nie jest żaden string, ale jest to buffer.
Po to pokazałam ci buffer, żebyś wiedział, że w tym momencie będzie to właśnie buffer.
Czyli możemy tutaj wykorzystywać wszystkie metody i właściwości, jakie znajdziesz w dokumentacji pod
buffer.
Czyli jest to właśnie bufor.
I teraz będę chciał go sobie wyświetlić w ten sposób. I to, co jest ważne, to taki stream,
gdybyśmy nie mieli tego zapisu, jest jakby tworzony, ale jest on w trybie paused, czyli zapauzowany.
I teraz aby uruchomić tryb flowing, czyli taki, który dane będzie odczytywał i przekazywał dalej,
możemy to zrobić na kilka sposobów.
Jednym z takich sposobów jest przypisanie eventu data. Czyli po przypisaniu data, od razu ten stream
zacznie odczytywać dane.
I ta funkcja będzie wykonywana za każdym razem, kiedy kawałek takich danych, czyli chunk, będzie dostępny
i przekazany tutaj jako bufor.
Więc wywołajmy sobie teraz node index i zobaczmy. Jak widzisz, w trzech takich kawałkach zostało to wszystko
odczytane i właśnie buffer został tutaj zwrócony.
Czyli możemy się odwołać do buffer toString, aby zobaczyć, co tak naprawdę tam zostało pobrane.
Zróbmy to raz jeszcze. I teraz będziemy nasze lorem ipsum widzieć zdekodowane właśnie w takich kawałkach
jako UTF-8.
Ok. To, co jest ciekawe, to chunk jest bufferem i buffer ma taką właściwość jak length.
Czyli możemy sobie tutaj kilka informacji wyświetlić w taki sposób.
Napisałem tutaj "wielkość kawałka" i odwołałem się do chunk.length, ale jeszcze sobie to dzielę przez 1024
i zaokrąglam,
po to, aby przekonwertować to na kilobajty i następnie ten chunk wyświetlamy. Może wyświetlmy go w takiej formie. Będziemy
widzieć, w jakichś kawałkach są odczytywane te dane. Nie bez powodu ten plik txt utworzyłem jako taki
plik większy, 140-kilobajtowy, aby na kilka takich chunków to było odczytywane. Zobaczmy. I widzimy, że wielkość
poszczególnych kawałków jest to 64 kilobajty.
No i ostatni kawałek tyle, ile zostało, czyli 12.
Gdybyś z jakiegoś powodu chciał zmienić tę wartość w jakich chunkach ma to być odczytywane, to możesz to
zrobić tutaj, podając specjalny obiekt konfiguracyjny. I mamy taką opcję jak hightWaterMark i tutaj możemy
podać w bajtach, ile chcemy, aby było ich na jeden raz odczytywanych.
Czyli podam sobie 32 razy 1024, a więc będzie to w tym momencie 32 kilobajty.
I zobaczmy, czy faktycznie tak będzie.
No i jak widzisz, rzeczywiście tak jest.
Mamy teraz kawałki po 32 kilobajty.
A co za tym idzie, było ich więcej niż wcześniej, kiedy odczytywaliśmy po 64 kilobajty.
Teraz pokażę ci jeszcze jedno bardzo ważne zdarzenie, które możemy przypisać do dowolnego streamu.
Jest to zdarzenie end.
Teraz go tutaj wstawię. I ono, jak możesz się domyślić, wywoła się dopiero wtedy, kiedy zakończymy odczytywanie
wszystkich danych.
Zobaczmy.
No i widzimy, że na samym końcu mamy "Odczytywanie pliku zakończone", czyli właśnie wtedy takie zdarzenie
jest wywoływane.
Gdybyśmy do pamięci chcieli zapisać sobie wszystko, co z takiego pliku jest zwracane, możemy skorzystać
z innych metod.
Ale gdybyśmy korzystali właśnie z takiego readStream, to moglibyśmy tutaj utworzyć jakąś zmienną, którą nazwalibyśmy
na przykład data.
Byłaby ona pusta na początku. Może w ten sposób.
No i teraz za każdym razem, kiedy mamy taki kawałek, moglibyśmy do tego data dodawać za pomocą +=
chunk.toString.
A wtedy, kiedy wywoła się end, to wiedzielibyśmy, że
w data mamy już wszystko, co tam się znajduje. Ale wtedy ta idea tego streamu, czyli aby nie wczytywać
wszystkich danych naraz do pamięci, byłaby tutaj nieprzestrzegana, czyli byłoby to troszeczkę bez sensu.
Ale w taki sposób mogłoby to zadziałać.
Natomiast teraz pokażę ci jeszcze, że możemy przekazywać streamy za pomocą pipe w inny sposób niż tutaj,
czyli nie musimy przypisywać zdarzenia on data czy on end.
Możemy cały taki plik odczytywać po kawałku i za każdym razem gdy mamy taki kawałek, przekazywać go gdzieś
dalej do streamu, który jest streamem writable, czyli zapisywalnym. Więc to sobie na razie wykomentuję w ten sposób
i pokażę ci, w jaki sposób to zrobić.
Zrobimy znowu trochę miejsca i pokażę ci, co jeszcze jest streamem. Dlatego że wspomniałem na początku tej
lekcji, że stream jest to taki abstrakcyjny interfejs.
No i pokazałem ci, jak utworzyć stream z pliku za pomocą fs.createReadStream.
Ale mamy jeszcze inne rodzaje streamów, a więc Node.js, jak wspomniałem, w wielu modelach dziedziczy sobie
z tego abstrakcyjnego interfejsu. I teraz mamy coś takiego jak process.
Następnie standard output.
Może łatwiej będzie to pokazać w tym miejscu, jeżeli przejdziemy sobie do interfejsu REPL. Mamy process, o nim jeszcze
będziemy mówić w tym kursie niebawem. Natomiast process przechowuje nam coś takiego jak stdout, czyli standard
output.
Za każdym razem kiedy wykonywaliśmy console.log i ten console.log pojawiał się nam tutaj, to właśnie pojawiał
się w standardowym wyjściu. Czyli w programowaniu mamy coś takiego jak standard input i standard output, a
także standard error. Czyli proces standard output
w tym przypadku jest to nic innego jak writable stream, czyli dziedziczy po prostu z tej klasy.
Dlatego mogę napisać tutaj stdout.write, a więc korzystam z metody, która jest dostępna na stremach i
jeżeli przejdziesz tutaj do dokumentacji, to ją znajdziesz. I mogę sobie wpisać cokolwiek tutaj i zostało to wyświetlone
w tym miejscu.
Czyli właśnie proces stdout jest streamem, writable streamem, czyli takim zapisywalnym.
Dlatego teraz możemy do niego przekazać wszystko, co będzie z tego pliku odczytywane w taki oto sposób.
Odwołamy się do stream.
Następnie pipe i tutaj musimy podać jakiś inny stream, bo wiemy, że nasz stream to readable, czyli odczytywany.
I tutaj musimy podać stream, który jest zapisywlany, a więc np. process.stdout. I wspomniałem ci, że sama taka
linijka nie powoduje odczytu z pliku, ale możemy z tego trybu paused na flowing przestawić ten stream
za pomocą np. on data, ale również za pomocą pipe. Czyli kiedy powiemy pipe, to będzie to wszystko uruchomione.
Czyli po kawałeczkach takich 32-kilobajtowych ten plik będzie odczytywany i za każdym razem, gdy będziemy mieli
kawałek, będzie przekazywany do tego streamu, w którym jest process.stdout.
Zobaczmy, jak to będzie wyglądać.
Tutaj sobie wyjdziemy z tego REPL-a i wywołamy node index.
No i zauważ, że wszystko nam się tutaj wyświetliło od razu, czyli cały ten nasz plik po kawałeczkach.
Ale nic nie musieliśmy zrobić.
Zatem pipe działa w ten sposób, że będzie od początku do końca
wszystkie te informacje odbierał i na końcu po prostu zakończy poprzez end. Mówiąc end,
mam na myśli to, że jeżeli byśmy sobie utworzyli taki stream, który byłby writable, czyli zapisywalny, to możemy
do niego wpisywać poprzez write, ale kiedy chcemy zakończyć, to wpisujemy czy korzystamy z metody end.
Tutaj poprzez pipe stanie się to automatycznie.
I jeszcze z pipe będziemy w tym kursie korzystać, więc przyswoisz sobie, w jaki sposób może działać.
Teraz chciałbym ci pokazać kolejny przykład, abyś jeszcze lepiej zrozumiał, na czym polegają właśnie takie
streamy.
Będę chciał sobie zaimportować moduł konsoli. Do tej pory korzystaliśmy wielokrotnie z console.log, ale okazuje
się, że console jest również osobnym modułem, który możesz znaleźć tutaj.
Dlatego będę chciał sobie go zaimportować na samej górze.
W ten sposób. I teraz nową konsolę sobie będziemy chcieli utworzyć, dlatego nazwę ją myConsole i poprzez
new Console będziemy chcieli taką konsolę utworzyć. Ale jej musimy przekazać
po pierwsze jakby stream, do którego będą wczytywane wszystkie logi, a także błędy jako drugi parametr.
I teraz jak możemy to zrobić?
Domyślnie gdybyśmy tutaj przekazali process.stdout, to co widziałeś przed momentem, i process.stderror,
to działałoby to dokładnie tak samo, jak console.log, który robiliśmy do tej pory.
A gdybyśmy zrobili console.error, to byłoby to wstawiane w tym miejscu. W terminalu wyświetlałoby się
to tak czy siak tutaj.
Natomiast my nie chcemy, żeby to działało dokładnie tak samo jak zwykła konsola, bo nie po to ją tutaj
importowałem, dlatego utworzymy sobie nowe streamy.
I pierwszy z nich nazwę logs. Skorzystamy z fs.create i mieliśmy wcześniej readstream - do odczytywania,
a teraz zrobimy writestream - do zapisywania. I będę chciał w katalogu, w którym jesteśmy, utworzyć plik
logs.txt i teraz będę chciał utworzyć jeszcze drugą zmienną, którą nazwiemy errors i skorzystamy
dokładnie z tego samego.
Ok.
I są to streamy.
Mówiłem ci, że do konsoli musimy przekazywać streamy.
Process.stdout i process.stderr to są streamy, więc wszystko by działało, ale to, co utworzyliśmy powyżej, to
również są streamy.
Dlatego możemy je tutaj przekazać w ten sposób. I od tej pory w naszej aplikacji za każdym razem gdy
skorzystamy z myConsole i z wszystkich metod, które znasz z normalnej konsoli, bo ona po prostu jest modułem
konsoli, to będą zapisywane do tych dwóch plików.
Czyli spróbujmy sobie zrobić tutaj myConsole.log i napiszemy tutaj "Pierwsza wiadomość". Zróbmy sobie
również drugą wiadomość. A teraz zróbmy sobie myConsole.error.
Takie coś również jest dostępne, podobnie w przeglądarkach internetowych.
Tutaj napiszemy sobie "Wiadomość z błędem" i wykonajmy sobie ten plik.
Jak widzisz, w tym katalogu nie istnieją takie pliki, które ja tutaj przekazałem, ale zobaczymy, czy one
zostaną utworzone.
Mamy jakiś błąd.
Ok, otrzymaliśmy błąd, że console is not a constructor z tego powodu, że popełniłem błąd tutaj na górze po require("console")
musimy się jeszcze odwołać do Console w taki oto sposób.
Teraz jeszcze raz możemy to sobie wywołać.
Zobaczmy.
No i nie mamy tutaj żadnych błędów, ale pojawiły nam się dwa nowe pliki. I zauważ, że w logs mamy dokładnie
to, co sobie wpisaliśmy, używając myConsole.log, a w error mamy to, co wykonaliśmy z użyciem myConsole.error.
Czyli mam nadzieję, że na tym etapie zrozumiałeś już, na czym polegają takie streamy.
Streamy będą pojawiać się w różnych miejscach, bo one dziedziczą sobie po prostu z tej klasy w zasadzie inne
moduły, takie jak np. file system czy konsola.
One dziedziczą sobie ze streamów i dlatego to wszystko działa.
Streamem oprócz takich plików odczytywalnych, zapisywalnych może być np. process standard output,
jak do tej pory zobaczyłeś. I teraz na koniec szybko pokażę ci jeszcze jeden ciekawy przykład gzipa.
Dlatego zaimportuję sobie tutaj wbudowany moduł node, który nazywa się zlib.
W ten sposób się do tego odwołujemy.
Czyli biblioteka zlib i następnie jej metoda createGzip zwróci mi odpowiedni obiekt tutaj.
W zasadzie będzie to stream.
I teraz jak działa gzip?
Być może wiesz. W internecie kiedy wchodzimy na jakąś stronę i pobieramy sobie np. style CSS czy JavaScript,
to serwer je kompresuje zazwyczaj gzipem, przesyła - to wszystko mniej zajmuje,
jeśli chodzi o bajty - i przeglądarka sobie to rozpakowywuje i node właśnie zawiera bibliotekę zlib, która pozwala nam
robić gzipy i pokażę ci teraz, jak bajecznie proste jest to z wykorzystaniem streamów. Bo znowu tutaj gzip
dziedziczy sobie z tego interfejsu stream.
Czyli wyobraźmy sobie ten nasz plik, który mamy tutaj -text.
On ma 144 kb.
Chciałabym go teraz zgzipować można powiedzieć, czyli po prostu skompresować
takim algorytmem gzip. Utworzymy sobie zatem zmienną może w tym miejscu, nazwę ją compressed i będę chciał utworzyć
stream
zapisywalny, czyli fs.createWriteStream.
W ten sposób. Będę chciał ten plik nazwać text.txt.gz. Takie jest rozszerzenie gzipa. I teraz poniżej będziemy
chcieli odwołać się do naszego streamu, który będzie odczytywał ten plik. Tak jak wcześniej zrobimy mu pipe
i będziemy chcieli przekazać to do gzip. Czyli w tym miejscu.
Mówiłem ci, że gzip tutaj będzie streamem, czyli przekazujemy to, co odczytujemy z tego pliku do gzip.
Za każdym razem kiedy on otrzyma jakiś kawałek, to będzie go pakował.
I co jest ciekawe, to pipe możemy łączyć. Czyli to, co zwróci gzip,
jest to stream, który nazywa się transform, a więc taki transformujący. On otrzymuje tekst i będzie
go gzipował, czyli będzie go pakował.
I znów możemy skorzystać z pipe i przekazać to do kolejnego streamu. A kolejnym streamem zapisywalnym jest
ten, który utworzyliśmy tutaj, a więc compressed. W ten sposób.
No i jeżeli wykonamy taki kod, zobaczmy, to powinien nam się tutaj pojawić nowy plik.
Tak też się dzieje.
Jest to plik tym razem już spakowany. Zobaczmy, ile on zajmuje. No i możesz zobaczyć, że oryginalny plik
zajmował 144 kb, a ten spakowany gzipem, który widzisz tutaj, tylko 38 kb. Więc, jak widać,
wszystko działa jak należy.
Ten plik sobie usunę i pokażę ci jeszcze tylko jedną ciekawą rzecz. Otóż skoro streamy dziedziczą sobie z EventEmittera,
to możemy tutaj przypisywać zdarzenia.
Jednym z takich zdarzeń jest close, czyli wtedy kiedy wszystko już się zakończy, to możemy sobie wykonać
jakiś console.log.
Ja pokażę ci sztuczkę, która pozwoli nam stwierdzić, ile czasu był pakowany taki plik gzipem. Skorzystamy sobie
z console - zamiast log to console.time.
W ten sposób. I tutaj podajemy string gzip i kiedy następnym razem wywołamy console.timeEnd i przekażemy ten
sam string - w przeglądarkach również to działa - to zostanie wyświetlony czas, jaki upłynął od tego wywołania
do tego wywołania.
Więc zobaczymy sobie, jak to będzie wyglądać.
Jak widzisz, 28 milisekund trwało pakowanie takiego pliku. W tej lekcji pokazałem ci wiele przykładów
streamów. Mam nadzieję, że dobrze je zrozumiałeś.
Czyli jest to abstrakcyjny interfejs, z którego nie korzystamy bezpośrednio, ale w wielu miejscach on
się pojawia, np. tutaj w file system czy w konsoli, czy jeszcze w wielu innych miejscach, które w tym kursie
zobaczysz.