Menu

Модуль path в Node.js: path.join и path.resolve

Модуль path собирает и разбирает пути к файлам по правилам операционной системы. path.join склеивает части пути, а path.resolve превращает их в абсолютный путь, начиная от текущей папки.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

path.join() соединяет части пути правильным разделителем и убирает .. и двойные слэши, но оставляет результат относительным, если первая часть была относительной. path.resolve() делает ту же работу справа налево и всегда возвращает абсолютный путь: он начинает от текущей рабочей папки, а любая абсолютная часть по пути начинает путь заново. Используйте join, чтобы собрать путь, и resolve, когда нужен абсолютный.

Программа вывела users/ada/notes.txt, затем users/grace/todo.txt (.. убрал ada), затем /srv/app/public/index.html и src/lib/utils.js: лишние слэши и пустые части исчезли. join никогда не считает /public/ началом нового пути, это просто следующая часть.

path.join и path.resolve

Разница видна, когда часть начинается с / или когда первая часть относительная:

С /public в середине join выдал /srv/app/public/img, а resolve отбросил всё, что было до неё, и выдал /public/img; .. оба обработали одинаково. Последние две строки показывают, что join оставил public/index.html относительным, а resolve сделал его абсолютным, подставив впереди текущую папку.

path.joinpath.resolve
РезультатОтносительный или абсолютный, как на входеВсегда абсолютный
Часть, начинающаяся с /Просто ещё одна частьПуть начинается с неё заново
Точка отсчётаНетТекущая рабочая папка
Без аргументов'.'Текущая рабочая папка
Типичное применениеСобрать путь из частейПревратить ввод пользователя или конфиг в абсолютный путь

basename, dirname, extname и parse

Эти функции разбирают путь на части. Они смотрят только на строку: файл может и не существовать.

Программа вывела cat.final.jpg, cat.final, /home/ada/photos и .jpg: extname берёт только часть после последней точки. path.parse возвращает всё сразу (root, dir, base, ext, name), а path.format превращает такой объект обратно в строку.

__dirname и __filename

В файле CommonJS __dirname это абсолютный путь к папке, где лежит файл, а __filename путь к самому файлу. Относительный путь вроде 'data.json' отсчитывается от того места, где пользователь запустил node, и ломается, как только кто-то запустит скрипт из другой папки. Собирайте пути от __dirname:

Программа вывела config.json dark и index.js. Полные пути на каждой машине разные, именно поэтому их собирают через path.join(__dirname, ...), а не пишут вручную.

__dirname в ES модулях: import.meta.dirname

В ES модулях нет __dirname; обращение к нему бросает ReferenceError: __dirname is not defined in ES module scope. Начиная с Node.js 20.11, их заменяют import.meta.dirname и import.meta.filename:

Программа вывела index.js и data/users.json. В версиях Node.js до 20.11 получите папку из URL модуля: path.dirname(fileURLToPath(import.meta.url)), где fileURLToPath берётся из node:url.

Почему не склеивать пути строками

'data' + '/' + file работает в macOS и Linux, но ломается в Windows, где разделитель \, и никак не обрабатывает .., двойные слэши или пропущенный слэш. path следует правилам системы, на которой работает. Результат для Windows можно увидеть на любой машине через 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

Передайте результат path.join в fs.readFile, и один и тот же код прочитает файл на любой системе.

Частые ошибки

Ожидать, что extname знает о двойных расширениях. path.extname('archive.tar.gz') равен '.gz', а у файла с точкой в начале вроде .env расширения нет вовсе: path.extname('.env') равен ''. Если важен весь суффикс, проверяйте конец имени сами.

Надеяться, что join удержит пользователя в папке. path.join('/srv/app', '../../etc/passwd') равен '/etc/passwd'. Прежде чем читать файл, имя которого пришло в запросе, сделайте resolve пути и проверьте, что он всё ещё начинается с вашей папки.

Часто задаваемые вопросы

Чем __dirname отличается от process.cwd()?

__dirname это папка текущего файла с кодом, и она никогда не меняется. process.cwd() это папка, из которой пользователь запустил node. Они совпадают, только когда программу запускают из её собственной папки.

path.join или path.resolve вместе с __dirname?

Для path.join(__dirname, 'data.json') подходит любой, потому что __dirname уже абсолютный. Берите resolve, когда какая-то часть сама может быть абсолютной, например путь, который ввёл пользователь, и вы хотите, чтобы победил именно он.

Как использовать модуль path с URL?

Не используйте: в URL всегда /, и у них свои правила для .. и строки запроса. Используйте класс URL, например new URL('../img/logo.png', 'https://example.com/docs/page').

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

Начать