Робота з файлами

Модуль FileSystem

Модуль FileSystem відповідає за роботу з файлами в Node.js.

const fs = require('fs');

Сучасна ініціалізація модуля з промісами відбувається наступним чином:

const fs = require('fs').promises;

Методи, які найбільше використовують для основних операцій з файлами:

fs.readFile(filename, [options]) - читання файлу

fs.writeFile(filename, data, [options]) - запис файлу

fs.appendFile(filename, data, [options])- додавання у файл

fs.rename(oldPath, newPath) - перейменування файлу

fs.unlink(path, callback) - видалення файлу

Синхронна робота з файлами

Методи бувають синхронні і асинхронні (про це вказує його назва - закінчується на Sync). Краще вживати без Sync у назві, бо якщо буде великий файл для читання, то дія заблокується поки не обробиться процес читання.

fs.readFileSync();
fs.readFile();

Потрібно підключати так:

const fs = require('fs');

Але в такому разі всі підключені функції не повертають проміс і їм необхідний callback. Синхронним функціям callback не потрібен, бо вони є блокувальними і тому вони не рекомендовані до застосування, тільки якщо це вимагає поточне завдання, і ви чітко розумієте навіщо ви їх використовуєте.

JS-доки

Не забувайте при написанні скриптів писати js-доки (коментарі). В майбутньому це допоможе згадати, що виконує скрипт, або допоможе в цьому розібратися іншим розробникам.

/**
 * read and write operations
 */

Також можна вказувати яке значення повертатиме функція.

/**
 * read and write operations
 * @returns {Promice<void>}
 */

void - в даному випадку означає null або undefined. Можна вказати, що функція повертатиме number або string тощо.

Читання файлу. Buffer

При роботі з файлами всі функції мають бути асинхронні. Бо ми не можемо знати наперед коли буде результат опрацювання команди. Те ж саме стосується роботи з мережею або запитом на REST API.

Також не забуваємо обробляти помилки. Використовуємо синтаксис .then().catch(); або async/await в поєднанні з try{} catch(){}

Створимо невеликий текстовий файл readme.txt, в якому запишемо якусь невелику фразу. Тоді функція читання цього файлу виглядатиме так:

fs.readFile('readme.txt')
  .then(data => console.log(data))
  .catch(err => console.log(err.message));

або ось так

const fs = require('fs').promises;

const readMyFile = async () => {
  try {
    const data = await fs.readFile('readme.txt');
    console.log(data);
  } catch (err) {
    console.log(err);
  }
};

readMyFile();

Параметр data у функції readFile, містить об'єкт типу Buffer. Це - послідовність прочитаних байтів (тобто сирі дані).

Отримання валідних даних при читанні

Найпростіший спосіб працювати з отриманими даними як з рядком - необхідно конвертувати data методом toString().

fs.readFile('readme.txt')
  .then(data => console.log(data.toString()))
  .catch(err => console.log(err.message));

або ось так

const fs = require('fs').promises;

const readMyFile = async () => {
  try {
    const data = await fs.readFile('readme.txt');
    const txt = data.toString();
    console.log(txt);
  } catch (err) {
    console.log(err);
  }
};

readMyFile();

Після такої операції ми вже отримаємо не бінарний код, а зрозумілий рядковий формат даних.

Опрацювання шляхів до файлу

Припустимо, що файл, який потрібно зчитати зберігається у папці documents, яка своєю чергою лежить у папці files.

myProject
        ├── index.js
        └── files
                └── documents
                            └── readme.txt

В такому разі нам доведеться прописати повний шлях до файлу.

const data = await fs.readFile('files/documents/readme.txt');

Утім тут є складність в тому, що операційні системи macOS та Unix проставляють в шляхах такий слеш /, а в операційній системі windows такий \. Тому код шляху, який працюватиме в одні ос, не працюватиме в іншій. Для вирішення цього ньюансу є модуль path.

Підключення:

const path = require("path");

В такому разі ми просто через кому передаємо кожен рівень шляху до файлу, а цей модуль path поставить правильні слеші за нас.

const fs = require('fs').promises;
const path = require("path");

const readMyFile = async () => {
  try {
    const pathToFile = path.join("files", "documents", "readme.txt");
    const data = await fs.readFile(pathToFile);
    const txt = data.toString();
    console.log(txt);
  } catch (err) {
    console.log(err);
  }
};

readMyFile();

Читання/запис JSON-файлу

Припустімо, що маємо таку структуру проєкту

myProject
        ├── index.js
        └── files
                └── sample.json
files/sample.json
{
  "a": 123,
  "b": "Mike",
  "c": true,
  "d": "Helen"
}

Зчитаймо вміст sample.json, внесемо зміни та збережемо у новий файл newJson.json на рівні з index.js.

index.js
const fs = require("fs").promises;
const path = require("path");

const readWriteJson = async () => {
  try {
    const pathToJson = path.join("files", "sample.json");
    const jsonResult = await fs.readFile(pathToJson);
    console.log(jsonResult); // повернеться бафер
    const json = JSON.parse(jsonResult);
    console.log(json); // повернеться json

    json.e = "Helga"; // внесемо зміни
    // створимо новий файл
    await fs.writeFile("newJson.json", JSON.stringify(json));
  } catch (err) {
    console.log(err);
  }
};

readWriteJson();

Читання каталогу

У цьому прикладі ми будемо використовувати два методи readdir (для читання директорії) і stat (для отримання статистики).

Напишімо скрипт files.js, який читатиме поточний каталог та виводити його вміст: ім'я файлу, його розмір та дату останньої зміни.

files.js
const fs = require('fs').promises;

fs.readdir(__dirname)
  .then(files => {
    return Promise.all(
      files.map(async filename => {
        const stats = await fs.stat(filename);
        return {
          Name: filename,
          Size: stats.size,
          Date: stats.mtime,
        };
      }),
    );
  })
  .then(result => console.table(result));

__dirname - повертає абсолютний шлях поточного каталогу.

У наслідку виконання промісу в змінній files міститься масив імен файлів та директорій з поточної директорії. Потім ми повертаємо масив промісів з аналізом кожного елемента цього масиву.

Провести аналіз допомагає змінна stats, де міститься інформація про поточний файл. Тут повертається ім'я файлу, час останньої зміни stats.mtime та розмір у байтах stats.size.

Результат виконання промісу, змінна result, передається функції console.table. В результаті на екран виводиться таблиця виконання скрипту в консолі.

Різновидом отримання статистики про файл є метод lstat.

 fs.lstat(pathToFile);

Корисним методом також буде перевірка чи є елемент в директорії папкою чи файлом isDirectory.

console.log(dirStat.isDirectory());

Покликання:

Node fs docs

Node path docs

JSDoc

Last updated