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.
Kontynuujemy pracę nad naszą aplikacją do grupowej zmiany nazw plików.
Teraz mamy krok trzeci - przefiltruj tylko te z podanym rozszerzeniem. Mamy już otwarty katalog i pod files
znajdują się wszystkie pliki, w zasadzie nazwy plików, które znajdują się w tym katalogu.
Dlatego może za pomocą console.log je wyświetlmy, aby wiedzieć, co tutaj się dzieje. Będę chciał przekazać dokładnie
te parametry, które wcześniej, abyśmy nie mieli błędu. I zobaczmy. Zauważ, że pojawia nam się błąd, dlatego że
nie istnieje katalog files.
Dlatego się tak dzieje, że mamy tutaj błąd -
otóż pod dir chcę przekazać images, bo właśnie w images znajdują się wszystkie obrazki. Wykonajmy sobie to teraz
i jak widzisz, nie mamy już błędu.
Wszystkie pliki zostały wczytane w tym miejscu, a w zasadzie ich nazwy i np. na Macu jest taki ukryty
plik jak DS_Store - również się tutaj pojawił - a także katalog images.
Teraz będę chciał przefiltrować, abyśmy mieli tablicę wyłącznie z tymi plikami, które spełniają rozszerzenie
podane pod ext.
W tym miejscu. Filtrowanie takiej tablicy jest dziecinnie proste
z tego powodu, że w JavaScript na tablicach mamy metodę filter. Ona przyjmuje parametr... jako parametr
pierwszy funkcję.
Czyli bardzo podobnie jak metoda forEach. I ta właśnie funkcja będzie dostawać kolejno każdy z elementów
tablicy.
Wiesz, że te elementy tablicy, które tutaj widzimy, to są stringi, czyli przeiterujemy i za każdym razem pod zmienną
file będziemy mieli jedną taką nazwę.
Czyli wystarczy, że sprawdzimy, czy ma ona jakby rozszerzenie png.
W jaki sposób można to zrobić?
Otóż posłużymy się tutaj taką ciekawą konstrukcją. Skorzystamy z path.
Natomiast jeszcze jedna ważna uwaga. Jeżeli z tej funkcji zwrócę true, to ten element, na przykład DS_Store pierwszy,
znajdzie się w nowej tablicy. Więc tutaj musimy utworzyć sobie zmienną, nazwę ją validFiles. I ona będzie zawierała
nową tablicę tylko z tymi elementami, przy których ta funkcja zwróciła wartość true.
Czyli to jest bardzo ważne.
I teraz tak. Możemy odwołać się do argv, czyli do naszej funkcji, następnie get i ext. W ten sposób pobierzemy
sobie rozszerzenie podane w tym miejscu. Ale tutaj podajemy rozszerzenie bez kropki, więc zaraz to będziemy
chcieli porównać. Tutaj zrobimy return i jakąś wartość - zaraz ją tutaj wstawiamy - chcę porównać do tego rozszerzenia.
Czyli chcę sprawdzić, czy w tym file końcówka, czyli png - w tym przypadku bez tej kropki, czy jest równe png,
czyli temu. I pod to value podstawimy sobie coś takiego.
Odwołamy się do modułu path, który mamy zaimportowany i następnie ma on fajną metodę, która nazywa się
extname.
Możemy tutaj podać dowolną ścieżkę i zostanie nam zwrócone rozszerzenie takiego pliku.
Skoro mamy pod zmienną file poszczególne pliki, np. black.png - on będzie tutaj - to przekażę file.
Tylko że ta metoda zwraca nam rozszerzenie z kropką, czyli ona zwróci np. .png i będę chciał to
porównać do png.
Dlatego metodą slice, bo to będzie string, więc na stringu mamy taką metodę, będę chciał sobie odciąć jeden
znak, czyli tę pierwszą kropkę.
Teraz zostanie zwrócone,
w niektórych przypadkach png, przy DS_Store
będzie to po prostu DS_Store. I będziemy to porównywać w tym miejscu.
Czyli jeżeli to się wykona, to pod console.log(validFiles) powinniśmy mieć tylko te, które spełniają podane w parametrze
rozszerzenie, czyli png.
No i rzeczywiście tak jest.
Teraz będziemy chcieli sobie te pliki posortować według daty i później zmienić ich nazwę.
Natomiast jak widzisz, nie mamy tutaj żadnej informacji o dacie takich plików, bo odczytując pliki z katalogu,
mamy wyłącznie ich nazwy jako stringi zapisane.
Dlatego będziemy musieli skorzystać z fs.stat, czyli z metody, którą również już znasz, a która służy do odczytywania
informacji o plikach.
Następnie w tych informacjach znajdziemy datę i będziemy porównywać datę, sortując całą tablicę.
Dlatego teraz przejdziemy do kroku czwartego i będę chciał go wkleić w tym miejscu. I w tym celu
utworzymy sobie kolejny moduł. Mamy już jeden moduł, który nazywa się argv. Chcę utworzyć kolejny moduł, który nazwę
utils - będą to takie pomocnicze metody.
Dlatego teraz w katalogu 18 utworzymy plik utils.js.
W ten sposób. I tutaj utworzymy metodę, która pozwoli nam to wszystko porównać.
Wróćmy jednak do pliku index.js, bo chcę ci pokazać, w jaki sposób będziemy to wywoływać.
Czyli chciałbym zmienną validFiles, którą mamy tutaj, nadpisać tym, co zwróci mi metoda sortByDate.
Będę chciał tam przekazać tę tablicę, a następnie katalog files.
Po co? Zobaczysz za moment.
Czyli taką metodę chciałbym gdzieś mieć.
No i skąd ją mogę wziąć?
Otóż na samej górze utworzymy sobie stałą sortByDate.
W ten sposób. I następnie poleceniem require będę chciał moduł utils, który za moment uzupełnimy, z tego
katalogu zaimportować i z niego wyciągnąć sobie metodę sortByDate.
Możemy zrobić to w tym miejscu. Ok.
I ona będzie dostępna tutaj, a więc będzie się można do niej odwołać.
Dlatego teraz będę chciał ją utworzyć w tym pliku.
Ona będzie wyglądać następująco. Już za sekundę ci ją wytłumaczę, ale wyeksportujmy ją najpierw. Module.exports
i tutaj znowu będzie obiekt.
Chciałabym mieć tutaj metodę sortByDate, bo już ją zaimportowaliśmy i ona będzie kierować właśnie na tę
funkcję.
Czyli tutaj te nazwy są takie same. To jest właściwość, którą zaimportowaliśmy sobie w tym miejscu.
A to jest nazwa funkcji, która znajduje się tutaj.
W ten sposób ją wyeksportujemy i ona przyjmuje tablicę z tymi plikami, my tę tablicę widzimy tutaj, a
następnie dir, a dir to będzie katalog images. On jest nam do ścieżki potrzebny. W JavaScript tablicę możemy sortować
za pomocą metody sort i ja to tłumaczę bardzo dokładnie w kursie JavaScript od podstaw. Aby sortować, to z takiej
funkcji musimy sobie zwrócić wartości dodatnie, ujemne lub 0 w zależności od tego, czy dany element ma być przed
innym elementem, czy też nie.
Mam nadzieję, że wiesz, jak działa sortowanie. Generalnie pod zmiennymi A i B będziemy mieli kolejne elementy.
Czyli na początku black i blue. Zostanie to posortowane i kolejne elementy będą tutaj brane pod uwagę.
Natomiast od nas zależy, wedle czego chcemy sortować. I skoro pod A i B
my będziemy mieli tylko string, taki jak black czy blue,
to trudno byłoby po tym sortować. Jedynie moglibyśmy to zrobić alfabetycznie.
Natomiast z tego black i z tego blue, czyli ze zmiennych A i B chcę odczytać informacje o pliku i dopiero
na podstawie tych informacji tutaj coś zwrócić.
Dlatego skorzystamy tutaj ze znanej nam już metody stat, ale nie będzie ona asynchroniczna. Bo gdybyśmy
napisali stat, to musiałbym tutaj podać funkcję callback, która się wykona i wtedy byłoby to bardzo trudno
zrobić.
Dlatego my skorzystamy tutaj z metody statSync, która pozwoli nam odczytać informacje o pliku, blokując
wykonywanie dalszego kodu.
Czyli kiedy będą te informacje, dopiero wtedy przejdziemy do kolejnej linijki. I w tym przypadku jest
to jak najbardziej pożądany przez nas efekt.
Jak widzisz, łączę sobie modułem path, który również musimy tutaj sobie zaimportować - const path. I również moduł fs, więc za pomocą path łączę
sobie dir.
Dlatego to przekazałem w tym miejscu.
Jesteśmy tutaj i przekazaliśmy sobie dir. U nas dir
to było właśnie images.
Chcę sobie to połączyć z nazwą pliku, czyli np. A to będzie black.
W ten sposób to wszystko zostanie odpowiednio połączone.
Informacje zostaną odczytane.
No i wśród informacji, to również ci już pokazywałem, mamy coś takiego jak birthtime.
Jest to obiekt daty, a obiekty daty mają coś takiego jak metoda getTime, która zwraca liczbę milisekund
od 1970 roku, od pierwszego stycznia.
Więc im starszy plik, tym będzie miał większą liczbę tych milisekund.
Dlatego możemy to porównać za pomocą po prostu znaku minus.
Czyli jeżeli wyjdzie wartość minusowa, to ten element będzie występował przed tamtym elementem itd.
To jest takie proste sortowanie. Możemy sortować na podstawie czegokolwiek tutaj chcemy. My sortujemy
na podstawie daty właśnie utworzenia takich plików.
No i co jest ciekawe, nie musiałbym na końcu zwracać return files i przypisywać tego do zmiennej. Wystarczyłoby
to zrobić w ten sposób, bo przekazaną tablicę sortujemy i ona jest sortowana, jest przekazywana przez
referencje, czyli poniżej validFiles już będzie posortowane.
Ale wydaje mi się, że trochę jest to czytelniejsze, jeżeli nadpisuję tę zmienną, dlatego zwracam sobie jeszcze tę
tablicę tutaj. Możemy sprawdzić, jak to będzie wyglądać,
wyświetlając sobie console.log tutaj, validFiles,
czyli nieposortowane. I teraz sortujemy tablicę i wyświetlamy ją raz jeszcze. Zobaczymy, czy będzie jakakolwiek
różnica.
No i jak widzisz, w tym przypadku nie ma różnicy.
Ale to wynika wyłącznie z tego, że kiedy kopiowałem cały ten katalog z plikami, to one zostały jakby w
tym momencie od nowa utworzone i mają taką samą datę utworzenia, w tym samym momencie.
Natomiast jeżeli sobie wstawisz jakieś swoje pliki, to zobaczysz, że one będą posortowanie, np. w folderze
alfabetycznie, ale kiedy posortujesz je tutaj po dacie, to zdecydowanie to się zmieni. Czyli zapewniam cię, że wszystko
działa poprawnie, tylko te pliki wypadałoby, żeby miały różną datę utworzenia.
Może się ona różnić nawet o kilka milisekund i to wystarczy.
W każdym razie posortowaliśmy sobie tę tablicę po dacie. Jak widziałeś, można to zrobić za pomocą już znanych
nam modułów.
I teraz przejdziemy sobie do kolejnego kroku, a jest to krok piąty, czyli w zasadzie już ostatni.
Chcemy zmienić nazwę każdego pliku na format, zmieniając dolary np. na 001.
Być może najbardziej skomplikowany krok. Zapiszemy sobie to w komentarzu i przejdziemy do jego realizacji.
Ten krok rozpoczniemy po prostu od tego, aby posortowaną już tablicę, w której mamy elementy po dacie posortowane,
przeiterować, a więc validFiles, następnie forEach.
Jest to funkcja dostępna na tablicach i ta funkcja będzie przyjmować file, czyli taki pojedynczy string
i następnie index od zera, czyli dla każdego elementu tej tablicy chcemy sobie wykonać ten kod, który znajduje
się wewnątrz. I teraz znasz już metodę, która służy do zmiany nazwy plików.
Jest to metoda rename, ale my tutaj skorzystamy z jej wersji synchronicznej, bo zauważ, że w ogóle nasz przykład -
i to jest bardzo ważne -
jest to aplikacja typu command line, którą sobie wykonujemy u siebie na komputerze, więc nie ma problemu, jeżeli
nie będziemy korzystać z metod asynchronicznych.
Gdyby ona w jakikolwiek sposób działała na serwerze, gdzie mogłoby się podłączać bardzo wielu klientów
jednocześnie, to wtedy musielibyśmy korzystać z metod asynchronicznych, aby ten serwer działał szybko.
O tym będzie później jeszcze w tym kursie. Natomiast jeżeli my tylko tutaj jednorazowo ją wykonujemy, to metody
synchroniczne jak najbardziej są ok.
No i dużo prościej jest w kodzie to zapisać.
Dlatego będę chciał się odwołać do fs, następnie do renameSync,
czyli to zostanie tutaj w tym miejscu już zrobione, ale pamiętasz, że może być tutaj błąd i musimy go
obsłużyć za pomocą try catch. Dlatego powiemy spróbuj -za moment to uzupełnimy - czyli zmienić nazwę pliku.
Jeżeli się nie uda, to przechwycić błąd w ten sposób i będziemy chcieli zwrócić ten błąd, wyrzucając go
po prostu za pomocą throw. Stworzę tutaj nowy obiekt błędu, czyli new error: "Nie można było zmienić nazwy pliku".
No i tutaj podamy sobie nazwę tego pliku - mamy ją tutaj - i następnie błąd to będzie error message. Czyli z tego
oryginalnego błędu będę chciał to przekazać. Cały ten string z kolei będzie pod tym nowym obiektem error
zwrócony i tam byśmy się mogli do error message odwołać.
Ale to się wyświetli bezpośrednio w konsoli, więc nie będziemy tego robić.
Ok.
Teraz chcemy zmienić nazwę pliku, który mamy pod file, więc musimy tutaj do niego pełną ścieżkę pobrać,
bo pod file będziemy mieli na przykład black.png.
Łatwo to będzie zrobić - path.join(dir), czyli nasz katalog, który mamy tutaj już u góry.
W tym miejscu. Będziemy chcieli go połączyć z nazwą pliku, czyli file i teraz tutaj będzie najważniejsza
rzecz, którą wyeksportujemy do modułu utils.
Otóż chcę tutaj podać nową nazwę pliku według formatu, który podaliśmy, czyli tam plik nazywał się
black.png -
ten pierwszy. Ale chcę go zamienić na photo i np. 001, bo tak sobie to tutaj podałem.
Więc jak będziemy mogli to zrobić?
Otóż chciałbym mieć jakąś metodę, która w tym miejscu zwróci photo001,
w kolejnej iteracji tej pętli photo002 i tak dalej.
I chciałbym, żeby to wyglądało w ten sposób.
Path.join, bo tutaj musimy podać ścieżkę do nowego pliku całą, dlatego chcę połączyć dir i następnie z nową
nazwą pliku, a ją będę chciał pobrać za pomocą funkcji, którą nazwiemy newFileName - za moment ją utworzymy.
No i tutaj do tej metody, do tej funkcji newFileName musimy podać format, czyli np. photo
i te trzy dolary. Format wiesz, że możemy sobie pobrać za pomocą argv.get(format), bo otworzyliśmy to już
wcześniej. Ta funkcja newFileName będzie jednak potrzebować jeszcze rozszerzenia takiego pliku, dlatego możemy
ją podać.
W ogóle to możemy sobie to sformatować tak, aby było lepiej widoczne, gdzie co się dzieje.
NewFileName jest tutaj, więc odwołujemy się do argv.get(ext), czyli extension.
Za moment to się zrobi jasne. Extension to w tym przypadku jest png i ostatnią rzeczą, jaką przyjmie ta metoda
będzie indeks, który mamy tutaj w pętli, czyli 0, 1, 2 itd. To jest ważne, abyśmy mogli te dolary zamienić
w odpowiedni sposób.
Dlatego wpiszę tutaj index.
Mam nadzieję, że to jest widoczne.
Otóż chcemy teraz utworzyć - to będzie ostatni krok - funkcję newFileName, która otrzyma format taki, jak jest
tutaj.
Następnie otrzyma rozszerzenie takie, jak podaliśmy tutaj i ostatni parametr jest to indeks, który jest
z kolei w tym miejscu w tej pętli forEach.
Tak to będzie wyglądać.
Dlatego newFileName u góry chciałbym sobie zainkludować i będzie to wyglądać w ten sposób.
Czyli w module utils będziemy mieli jeszcze jedną taką metodę jak newFileName. Czyli w tym pliku już tak naprawdę
skończyliśmy. Przejdźmy tutaj.
Utworzymy sobie taką funkcję newFileName - ona będzie przyjmować dokładnie te parametry, które przed momentem jej
przekazaliśmy, czyli format, extension i index.
I chciałbym ją tutaj jeszcze wyeksportować. NewFileName równa się newFileName, bo tam ją sobie importowaliśmy.
Troszkę tutaj ciasno, więc podniesiemy to i teraz tę funkcję uzupełniły. Już ci wytłumaczę, na czym ona
polega.
Generalnie pod format będziemy mieli coś takiego, czyli np. photo i trzy dolary
tutaj zapisane. Te dolary są wymagane akurat w naszej aplikacji, ale już nie walidujemy, czy ktoś je podał
i pod index będziemy mieli w tym przypadku np. zero,
jeżeli to będzie pierwszy element. I teraz chciałbym w tym stringu format zamienić wyrażeniem regularnym
te wszystkie dolary, które tutaj są, na 001. Jak możemy to zrobić? Stworzę zmienną formatted i odwołamy się
do format.replace. czyli w JavaScript każdy string ma taką metodę jak replace.
Jeżeli format to będzie photo i 3 dolary, to powiem, że chcę zamienić te 3 dolary na coś, czyli tutaj musimy podać wyrażenie
regularne, czego szukamy. Wyrażenia regularne jeżeli dla ciebie są straszne, to po prostu przepisz to, co
ja tutaj piszę, a jeżeli chcesz się dowiedzieć, jak one dokładnie działają, to tłumaczę je od podstaw w
kursie JavaScript w praktyce. Podamy sobie tutaj wyrażenie regularne, które będzie wyglądać następująco.
Chcę utworzyć grupę i następnie chcę znaleźć dolara.
Ale że dolar ma specjalne znaczenie w wyrażeniach regularnych, to zrobimy mu w ten sposób.
Czyli tutaj mówię: znajdź mi w tym stringu format - będzie to u nas taki string - wszystkie dolary, dlatego
że podam tutaj plusa, a plus oznacza, że jedno lub więcej wystąpień takiego dolara.
I to wszystko zostanie zapisane w takiej grupie. I tutaj jako drugi parametr zazwyczaj podajemy to, co
ma zostać zamienione, czyli mówię np. znajdź wszystkie dolary -
mógłbym tak zrobić - i zamienić na jeden.
Jeżeli tak bym zrobił, to pod formatted,
czyli w tym miejscu, będziemy mieli photo-111, albo w zasadzie nawet źle ci powiedziałem - będziemy
mieli photo-1.
Dlatego że te trzy dolary zostaną zamienione na jedynkę. Ale jeżeli chcemy coś zrobić dynamicznie, to
możemy jako drugi parametr podać funkcję i ta funkcja przyjmuje match - zaraz ci pokażę, co to jest. I to,
co ona zwróci, to zostanie jakby wstawione w tym miejscu tego stringu, który przed momentem tutaj wpisałem.
Czyli dynamicznie możemy również to zrobić. I teraz aby ci pokazać, jak to dokładnie będzie działać, to zrobimy
tutaj console.log(match), ale nie wywołamy tego w Node, bo mielibyśmy błąd, tylko otworzę sobie przeglądarkę
Chrome. Będąc na stronie Google, przejdziemy sobie do konsoli - tutaj. I wkleję taką funkcję jak newFileName. I teraz z niej skorzystamy.
Tutaj nie skorzystaliśmy jeszcze z niczego, co jest jakby specyficzne w Node. Jest to zwykły JavaScript, więc
w przeglądarce również zadziała. Podamy je jako format to, co my będziemy tam mieli przekazane, czyli
photo-$$$, ok, rozszerzenie to png, na razie nie jest to konieczne i index to np. zero.
I chcę ci pokazać ten console.log, który ona zrobi tutaj, czyli match z wyrażeń regularnych. Jak widzisz, jest
to dokładnie trzy dolary. Czyli to wyrażenie regularne znalazło te trzy dolary i to one będą zastępowane,
czyli w tym miejscu pod match
mamy takie trzy dolary. I teraz pozostało nam zrobić jedną bardzo ciekawą rzecz. Otóż jak z takich trzech
dolarów - bo wiesz, że tutaj możemy podać cztery dolary, możemy jeden.
Jak zrobić tak, aby dwa pierwsze z nich zamienić na 00 (na przykład) i trzeci na index podany tutaj, czyli
np. będzie to 1.
Wiem, że może na razie jest to skomplikowane. Ja wkleję gotowy kod, postaram się go wytłumaczyć.
Jeżeli mi się to nie uda, to po prostu krok po kroku spróbuj go sobie przeanalizować z użyciem console.log.
Na początek utworzę zmienną diff. I teraz match - widziałeś w przeglądarce, że są to np. trzy dolary, więc
jest to string.
Jeżeli odwołam się do match.length, to zostanie mi zwrócona trójka. Czyli chcę się tutaj odwołać do tej trójki
i od tego odjąć index, który
tutaj mamy - length.
No, ale index, który przekazujemy - i w ogóle zdałem sobie sprawę, że tutaj na końcu, gdzie przekazywałem index, to chciałbym
do niego dodać 1, abyśmy od jedynki numerowali, bo tutaj na początku będzie 0. Skoro index to
będzie 1,
nie możemy odwołać się do 1.length, bo jest to number. Ale jeżeli konstruktorem takim string zamienimy,
podamy tutaj index, to zostanie w tym miejscu zwrócona jedynka, ale to już nie będzie jeden tylko jeden.
Mam nadzieję, że jest to jasne. Więc można się wtedy odwołać do length. Jeżeli się odwołam do length, to w tym przypadku
jedynka zostanie zwrócona, bo mamy tylko jeden znak.
Ale gdyby to była 10, to będziemy mieli już dwa znaki i wtedy od tej trójki - bo mamy trzy dolary -
odejmiemy sobie tutaj dwójkę i wyjdzie nam jeden.
A to oznacza, że zero na początku chcę powtórzyć tylko raz.
I do tego dodać właśnie wartość index, czyli np. 010.
Zauważ, że jeżeli na początku tutaj index będzie miał wartość 1, to 1.length zwróci jedynkę, bo mamy tylko jeden
znak. Czyli tutaj będzie 3 minus 1 to jest 2.
Oznacza to, że chcę 0 powtórzyć dwa razy.
I do tego dodać tę jedynkę.
Taka będzie nazwa tego pliku na końcu.
Więc mam nadzieję, że teraz to widać. I teraz pozostało nam wymyślić, w jaki sposób możemy to zero powtórzyć
kilka razy.
Co jest ciekawe, to w ECMAScript 6, która jest w 99% wspierana tutaj w Node, możemy odwołać się na dowolnym stringu,
czyli np. gdybyśmy tutaj mieli test do metody repeat i napisać, że chcemy to powtórzyć trzy razy,
a to zwróci nam
testtesttest, czyli jeżeli tutaj napiszę zero, repeat,
ile razy go chcę powtórzyć,
no mamy to już tutaj w tym diffie, to zostanie dla mnie zwrócone np. 00. I do tego będę chciał sobie dodać
resztę.
Dlatego na początku zrobimy return, bo ta funkcja musi zwrócić to, czym będziemy chcieli te 3 dolary znalezione
w tym miejscu zastąpić. Zero chcę powtórzyć tyle razy, ile jest w diffie,
do tego dodać sobie index, czyli dokładnie to, co przed momentem u góry ci pokazywałem.
I to będziemy mieli pod zmienną formatted. Czyli pod formatted znajdzie się photo-pauza i te trzy dolary
zamienione na 00 np. 1.
Ale może się pojawić błąd, bo jeżeli to będzie wartość ujemna, to co wtedy?
Wyobraź sobie, że tutaj podalibyśmy tylko jeden dolar.
A to oznacza, że chcę zamieniać nasze pliki na photo-1, photo-2 itd.
Czyli mielibyśmy jeden dolar. W tym miejscu dla nas zostałaby zwrócona jedynka a index to byłoby np 10.
Czyli mielibyśmy wartość w tym przypadku, bo 10 to byłyby 2 znaki, tutaj byłby jeden znak, czyli 1
minus 2 to byłoby to -1 i tutaj będzie błąd. Czyli repeat nie możemy przekazać wartości ujemnej.
Dlatego sprawdzę sobie tutaj takim operatorem if skróconego, czy diff jest mniejsze od zera.
Jeżeli tak by się zdarzyło, to chcę tutaj przekazać 0, czyli nie chcę w ogóle tego zera powtarzać.
Pusty string zostanie zwrócony, a w przeciwnym wypadku jeżeli jest 0 lub większy, to będę chciał tutaj
podać diff.
Znowu nie ma tutaj żadnej magii.
Generalnie w tym miejscu pod formatted powinniśmy mieć już photo-001, 002.
Jeżeli podamy tutaj tylko 2 dolary, to na przykład 01. I to chcę zwrócić z tej funkcji formatted.
Do tego chcę dodać kropkę i następnie rozszerzenie, które sobie tutaj przekazaliśmy, czyli ext.
Ta metoda newFileName - teraz chcę ci przypomnieć - została wywołana w tym miejscu, gdzie sobie łączyliśmy ścieżkę,
czyli tak naprawdę podawaliśmy nową nazwę pliku i ona zwróci nam w to miejsce dokładnie to, co przed
momentem zrobiłem poprzez return.
I teraz zobaczmy, czy nam to zadziała.
Ja sobie najpierw zduplikuę jeszcze ten katalog images. Napiszemy images_copy. Ok.
I teraz wywołamy raz jeszcze to wszystko. Czyli znowu mówimy: znajdź wszystkie pliki w katalogu images.
Tylko te z rozszerzeniem png posortujemy po dacie i zamienimy to na photo.
W ten sposób. Zobaczmy.
Nie ma żadnego komunikatu o błędzie, a to znaczy, że prawdopodobnie wszystko poszło ok. I zobaczmy. Rzeczywiście
tak jest. Np. katalog images został pominięty, a pozostałe obrazki zostały poprawnie zamienione na takie
oto nazwy.
No i teraz będę chciał ten katalog usunąć,
nazwę tego katalogu zmienić z powrotem na images. Raz jeszcze wykonamy jego kopię.
I raz jeszcze wykonamy sobie to, co mamy tutaj, ale tym razem wpiszę tylko 2 dolary i zapewniam cię, że również
można to zrobić z jednym, ale przynajmniej jeden być musi.
Wpiszę teraz dwa dolary, zobaczmy, nie ma żadnego błędu, no i nazwy plików zostały zmienione w ten oto sposób,
jak możesz zobaczyć tutaj z boku. To już jest koniec naszego przykładu. Napisaliśmy swoją własną aplikację
typu Command Line Interface, która pozwala nam zmieniać grupowo nazwy plików na taki fajny podany format. Możesz
spróbować ją rozwinąć. Jeżeli nie wszystko dobrze zrozumiałeś, to powolutku sobie to jeszcze przerób.
Tak naprawdę najbardziej skomplikowane rzeczy, które na pierwszy rzut oka trudno zrozumieć, być może są
tutaj, w sortowaniu tej tablicy.
No i w tym oto miejscu. Ja również nie napisałem tego od razu prosto z głowy.
Troszkę poeksperymentowałem. Te błędy, które np. tutaj mogły się pojawić, u mnie się pojawiły, dlatego
skończyłem z taką wersją. Upewnij się, że wszystko dobrze rozumiesz i zapraszam cię już do kolejnego rozdziału.