Menu

Moduł path w Node.js: path.join vs path.resolve

Moduł path składa i rozkłada ścieżki plików zgodnie z zasadami systemu operacyjnego. path.join łączy segmenty; path.resolve zamienia je na ścieżkę absolutną, zaczynając od bieżącego folderu.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

path.join() łączy segmenty ścieżki właściwym separatorem i porządkuje .. oraz podwójne ukośniki, ale zostawia wynik względny, jeśli zacząłeś od segmentu względnego. path.resolve() robi to samo od prawej do lewej i zawsze zwraca ścieżkę absolutną: zaczyna od bieżącego katalogu roboczego, a każdy absolutny segment po drodze zaczyna ścieżkę od nowa. Używaj join do budowania ścieżki, a resolve, gdy potrzebujesz absolutnej.

Wypisało users/ada/notes.txt, potem users/grace/todo.txt (.. usunęło ada), potem /srv/app/public/index.html i src/lib/utils.js: nadmiarowe ukośniki i puste segmenty zniknęły. join nigdy nie traktuje /public/ jako nowego początku; to po prostu kolejny segment.

path.join vs path.resolve

Różnicę widać, gdy segment zaczyna się od / albo gdy pierwszy segment jest względny:

Z /public w środku join dało /srv/app/public/img, a resolve odrzuciło wszystko przed nim i dało /public/img; .. oba potraktowały tak samo. Dwie ostatnie linie pokazują, że join zostawiło public/index.html względne, a resolve zrobiło je absolutnym, dodając na początku bieżący folder.

path.joinpath.resolve
WynikWzględny albo absolutny, jak wejścieZawsze absolutny
Segment zaczynający się od /Po prostu kolejny segmentZaczyna ścieżkę od nowa
Punkt startowyBrakBieżący katalog roboczy
Bez argumentów'.'Bieżący katalog roboczy
Typowe użycieBudowanie ścieżki z częściZamiana danych od użytkownika lub konfiguracji na ścieżkę absolutną

basename, dirname, extname i parse

Te funkcje rozkładają ścieżkę na części. Patrzą tylko na string: plik nie musi istnieć.

Wypisało cat.final.jpg, cat.final, /home/ada/photos i .jpg: extname bierze tylko część po ostatniej kropce. path.parse zwraca wszystko naraz (root, dir, base, ext, name), a path.format zamienia taki obiekt z powrotem na string.

__dirname i __filename

W pliku CommonJS __dirname to absolutna ścieżka folderu, w którym leży plik, a __filename to sam plik. Ścieżka względna, np. 'data.json', jest liczona od miejsca, z którego użytkownik uruchomił node, i psuje się, gdy ktoś uruchomi twój skrypt z innego folderu. Buduj ścieżki od __dirname:

Wypisało config.json dark i index.js. Pełne ścieżki są inne na każdej maszynie i właśnie dlatego budujesz je przez path.join(__dirname, ...), zamiast je wpisywać.

__dirname w modułach ES: import.meta.dirname

Moduły ES nie mają __dirname; użycie go rzuca ReferenceError: __dirname is not defined in ES module scope. Od Node.js 20.11 zastępują je import.meta.dirname i import.meta.filename:

Wypisało index.js i data/users.json. W wersjach Node.js przed 20.11 wyznacz folder z URL modułu: path.dirname(fileURLToPath(import.meta.url)), z fileURLToPath z node:url.

Dlaczego nie składać ścieżek ze stringów

'data' + '/' + file działa w macOS i Linuksie, a potem zawodzi w Windows, gdzie separator to \, i nic nie robi z .., podwójnymi ani brakującymi ukośnikami. path stosuje zasady systemu, na którym działa. Wynik dla Windows zobaczysz na każdej maszynie przez path.win32:

const path = require('path');

path.posix.join('users', 'ada', 'notes.txt'); // 'users/ada/notes.txt'
path.win32.join('users', 'ada', 'notes.txt'); // 'users\\ada\\notes.txt'
path.sep;                                     // '/' on macOS and Linux, '\\' on Windows

Przekaż wynik path.join do fs.readFile, a ten sam kod odczyta plik w każdym systemie.

Częste błędy

Oczekiwanie, że extname zna podwójne rozszerzenia. path.extname('archive.tar.gz') to '.gz', a plik kropkowy, np. .env, w ogóle nie ma rozszerzenia: path.extname('.env') to ''. Sprawdzaj koniec nazwy sam, gdy liczy się pełny sufiks.

Ufanie, że join zatrzyma użytkownika w folderze. path.join('/srv/app', '../../etc/passwd') to '/etc/passwd'. Zanim odczytasz plik wskazany przez żądanie, rozwiąż ścieżkę i sprawdź, czy nadal zaczyna się od twojego folderu.

Najczęściej zadawane pytania

Czym różni się __dirname od process.cwd()?

__dirname to folder bieżącego pliku źródłowego i nigdy się nie zmienia. process.cwd() to folder, z którego użytkownik uruchomił node. Są takie same tylko wtedy, gdy program uruchomiono z jego własnego folderu.

path.join czy path.resolve z __dirname?

W path.join(__dirname, 'data.json') zadziała jedno i drugie, bo __dirname jest już absolutny. Użyj resolve, gdy segment sam może być absolutny, np. ścieżka wpisana przez użytkownika, i chcesz, żeby to ona wygrała.

Jak używać modułu path z URL-ami?

Nie używaj: URL-e zawsze używają / i mają własne zasady dla .. i query stringów. Użyj klasy URL, np. new URL('../img/logo.png', 'https://example.com/docs/page').

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

Zacznij