Пошук уроків, статей та іншого контенту
Навчитеся будувати, нормалізувати й безпечно обробляти шляхи до файлів за допомогою модуля path.
pathМодуль path входить до стандартної бібліотеки Node.js і допомагає працювати зі шляхами до файлів та каталогів.
Шлях залежить від операційної системи:
у Linux і macOS роздільником є /;
у Windows — \.
Модуль path самостійно використовує правильний формат для поточної операційної системи. Завдяки цьому не потрібно вручну конструювати шляхи через конкатенацію рядків.
const path = require('node:path');
const filePath = path.join('data', 'users', 'users.json');
console.log(filePath);На Linux результатом буде:
data/users/users.jsonНа Windows:
data\users\users.jsonДля використання сучасного синтаксису ES-модулів можна написати:
import path from 'node:path';path.join()Метод path.join() об'єднує окремі частини шляху та нормалізує результат.
const path = require('node:path');
const filePath = path.join('project', 'src', 'app.js');
console.log(filePath);Метод також обробляє зайві роздільники та спеціальні частини . і ..:
const path = require('node:path');
console.log(path.join('project', 'src', '..', 'app.js'));
// project/app.js
console.log(path.join('project', '', 'src', 'app.js'));
// project/src/app.jsТакий код може працювати неправильно на іншій операційній системі:
const directory = 'data';
const fileName = 'users.json';
const filePath = directory + '/' + fileName;Краще використовувати path.join():
const path = require('node:path');
const directory = 'data';
const fileName = 'users.json';
const filePath = path.join(directory, fileName);
console.log(filePath);Відносний шлях обчислюється від поточної робочої директорії процесу:
data/users.jsonАбсолютний шлях містить повний шлях від кореня файлової системи:
/home/user/project/data/users.jsonДля перевірки шляху використовується path.isAbsolute():
const path = require('node:path');
console.log(path.isAbsolute('/home/user/project')); // true
console.log(path.isAbsolute('data/users.json')); // falseУ Windows абсолютним також є шлях на кшталт C:\project\data.
path.resolve()Метод path.resolve() об'єднує частини шляху та повертає абсолютний шлях.
const path = require('node:path');
const filePath = path.resolve('data', 'users.json');
console.log(filePath);Якщо скрипт запущено з директорії /home/user/project, результатом буде:
/home/user/project/data/users.jsonНа відміну від path.join(), path.resolve() завжди повертає абсолютний шлях.
const path = require('node:path');
console.log(path.join('data', 'users.json'));
// data/users.json
console.log(path.resolve('data', 'users.json'));
// абсолютний шлях до data/users.jsonДля отримання шляху до поточного файлу часто використовують __dirname у CommonJS:
const path = require('node:path');
const configPath = path.join(__dirname, 'config', 'app.json');
console.log(configPath);__dirname — це абсолютний шлях до директорії, у якій розташований поточний файл.
Метод path.normalize() виправляє зайві роздільники та обробляє частини . і ...
const path = require('node:path');
const inputPath = 'project//src/../app.js';
const normalizedPath = path.normalize(inputPath);
console.log(normalizedPath);
// project/app.jsВажливо: path.normalize() не перетворює відносний шлях на абсолютний. Для цього потрібно використовувати path.resolve().
const path = require('node:path');
console.log(path.normalize('data/../users.json'));
// users.json
console.log(path.resolve('data/../users.json'));
// абсолютний шлях до users.jsonМодуль path має методи для отримання окремих частин шляху.
path.basename()Повертає останню частину шляху, зазвичай ім'я файлу:
const path = require('node:path');
const filePath = '/home/user/project/data/users.json';
console.log(path.basename(filePath));
// users.jsonДругим аргументом можна передати розширення, яке потрібно прибрати:
const path = require('node:path');
console.log(path.basename('/data/users.json', '.json'));
// userspath.dirname()Повертає шлях до директорії, у якій розташований файл:
const path = require('node:path');
const filePath = '/home/user/project/data/users.json';
console.log(path.dirname(filePath));
// /home/user/project/datapath.extname()Повертає розширення файлу разом із крапкою:
const path = require('node:path');
console.log(path.extname('users.json'));
// .json
console.log(path.extname('archive.tar.gz'));
// .gz
console.log(path.extname('README'));
// порожній рядокpath.parse()Метод path.parse() розділяє шлях на основні частини:
const path = require('node:path');
const parts = path.parse('/home/user/project/data/users.json');
console.log(parts);Результат матиме приблизно такий вигляд:
{
root: '/',
dir: '/home/user/project/data',
base: 'users.json',
ext: '.json',
name: 'users'
}Основні властивості:
root — коренева частина шляху;
dir — директорія;
base — ім'я файлу разом із розширенням;
ext — розширення;
name — ім'я файлу без розширення.
path.format()Метод path.format() виконує зворотну операцію до path.parse(): будує шлях з окремих частин.
const path = require('node:path');
const filePath = path.format({
dir: '/home/user/project/data',
name: 'users',
ext: '.json'
});
console.log(filePath);
// /home/user/project/data/users.jsonЯкщо вказані і base, і name з ext, властивість base має вищий пріоритет:
const path = require('node:path');
const filePath = path.format({
dir: '/data',
base: 'users.json',
name: 'other',
ext: '.txt'
});
console.log(filePath);
// /data/users.jsonpath.relative()Метод path.relative() визначає, як перейти від однієї директорії до іншої.
const path = require('node:path');
const from = '/home/user/project/src';
const to = '/home/user/project/data/users.json';
console.log(path.relative(from, to));
// ../data/users.jsonРезультат є відносним шляхом від from до to.
Якщо шляхи однакові, метод повертає порожній рядок:
const path = require('node:path');
console.log(
path.relative('/home/user/project', '/home/user/project')
);
// ''Якщо частина шляху надходить від користувача, не можна без перевірки додавати її до директорії застосунку.
Наприклад, користувач може передати:
../../secret.txtТакий шлях може вийти за межі дозволеної директорії. Це називається атакою обходу директорій, або path traversal.
path.relative()Надійний підхід:
визначити дозволену базову директорію;
побудувати абсолютний шлях до запитаного файлу;
перевірити, що файл залишається всередині базової директорії.
const path = require('node:path');
const baseDirectory = path.resolve(__dirname, 'public');
function getSafeFilePath(userPath) {
const basePath = path.resolve(baseDirectory);
const targetPath = path.resolve(basePath, userPath);
const relativePath = path.relative(basePath, targetPath);
const isOutsideBaseDirectory =
relativePath.startsWith('..' + path.sep) ||
relativePath === '..' ||
path.isAbsolute(relativePath);
if (isOutsideBaseDirectory) {
throw new Error('Доступ до цього шляху заборонено');
}
return targetPath;
}
try {
console.log(getSafeFilePath('images/logo.png'));
console.log(getSafeFilePath('../../secret.txt'));
} catch (error) {
console.error(error.message);
}Перший шлях залишиться всередині public, а другий буде відхилено.
Перевірка враховує:
шлях, який починається з ..;
шлях, що дорівнює ..;
абсолютний результат path.relative().
Не слід перевіряти безпеку лише через просту перевірку рядка:
if (filePath.startsWith(baseDirectory)) {
// Така перевірка може бути недостатньою
}Наприклад, директорії public і public-backup можуть мати однаковий початок у вигляді рядка, але бути різними директоріями.
Наступна програма будує шлях до файлу, показує його частини та перевіряє шлях, отриманий від користувача:
const path = require('node:path');
const publicDirectory = path.resolve(__dirname, 'public');
function resolvePublicFile(userPath) {
const targetPath = path.resolve(publicDirectory, userPath);
const relativePath = path.relative(publicDirectory, targetPath);
const isOutside =
relativePath === '..' ||
relativePath.startsWith('..' + path.sep) ||
path.isAbsolute(relativePath);
if (isOutside) {
throw new Error('Файл знаходиться за межами public');
}
return targetPath;
}
const requestedFile = 'images/logo.png';
try {
const filePath = resolvePublicFile(requestedFile);
console.log('Повний шлях:', filePath);
console.log('Директорія:', path.dirname(filePath));
console.log('Ім’я файлу:', path.basename(filePath));
console.log('Розширення:', path.extname(filePath));
} catch (error) {
console.error(error.message);
}Для запуску створіть файл, наприклад app.js, і виконайте:
node app.jsКаталог public та сам файл images/logo.png не обов'язково мають існувати: приклад працює саме з побудовою та перевіркою шляху.
/ або \const filePath = directory + '/' + fileName;Такий код залежить від конкретного формату роздільника. Використовуйте path.join().
join() і resolve()path.join() об'єднує частини та може повернути відносний шлях;
path.resolve() повертає абсолютний шлях.
Обирайте метод залежно від потрібного результату.
Шлях files/../secret.txt потрібно спочатку перетворити на абсолютний або нормалізований, а вже потім перевіряти. Інакше частини .. можуть приховати вихід за межі дозволеної директорії.
path.extname('archive.tar.gz') повертає .gz, а не .tar.gz. Якщо потрібна повна назва файлу, використовуйте path.basename() або path.parse().
Навіть якщо шлях виглядає як ім'я файлу, він може містити .. або бути абсолютним. Усі шляхи від користувача потрібно перевіряти відносно дозволеної базової директорії.
path — стандартний модуль Node.js для роботи зі шляхами.
path.join() об'єднує частини шляху.
path.resolve() створює абсолютний шлях.
path.normalize() прибирає зайві частини та роздільники.
path.basename(), path.dirname() і path.extname() повертають частини шляху.
path.parse() розділяє шлях на властивості, а path.format() збирає його назад.
path.relative() допомагає визначити розташування одного шляху відносно іншого.
Шляхи, отримані від користувача, потрібно перевіряти, щоб не дозволити вихід за межі потрібної директорії.