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.join | path.resolve | |
|---|---|---|
| Wynik | Względny albo absolutny, jak wejście | Zawsze absolutny |
Segment zaczynający się od / | Po prostu kolejny segment | Zaczyna ścieżkę od nowa |
| Punkt startowy | Brak | Bieżący katalog roboczy |
| Bez argumentów | '.' | Bieżący katalog roboczy |
| Typowe użycie | Budowanie ścieżki z części | Zamiana 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').