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

## Модуль FileSystem

Модуль <mark style="background-color:red;">FileSystem</mark> відповідає за роботу з файлами в Node.js.

```javascript
const fs = require('fs');
```

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

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

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

<mark style="background-color:red;">fs.readFile(filename, \[options])</mark> - читання файлу&#x20;

<mark style="background-color:red;">fs.writeFile(filename, data, \[options])</mark> - запис файлу&#x20;

<mark style="background-color:red;">fs.appendFile(filename, data, \[options])</mark>- додавання у файл&#x20;

<mark style="background-color:red;">fs.rename(oldPath, newPath)</mark> - перейменування файлу

<mark style="background-color:red;">fs.unlink(path, callback)</mark> - видалення файлу

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

Методи бувають синхронні і асинхронні (про це вказує його назва - закінчується на Sync). Краще вживати <mark style="background-color:red;">без Sync</mark> у назві, бо якщо буде великий файл для читання, то дія заблокується поки не обробиться процес читання.

```javascript
fs.readFileSync();
fs.readFile();
```

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

```javascript
const fs = require('fs');
```

Але в такому разі всі підключені функції не повертають проміс і їм необхідний <mark style="background-color:red;">callback</mark>. Синхронним функціям <mark style="background-color:red;">callback</mark> не потрібен, бо вони є блокувальними і тому вони не рекомендовані до застосування, тільки якщо це вимагає поточне завдання, і ви чітко розумієте навіщо ви їх використовуєте.

## JS-доки

{% hint style="info" %}
Не забувайте при написанні скриптів писати **js-доки** (коментарі). В майбутньому це допоможе згадати, що виконує  скрипт, або допоможе в цьому розібратися іншим розробникам.
{% endhint %}

```
/**
 * read and write operations
 */
```

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

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

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

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

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

Також не забуваємо обробляти помилки. Використовуємо синтаксис <mark style="background-color:red;">.then().catch();</mark> або <mark style="background-color:red;">async/await</mark> в поєднанні з <mark style="background-color:red;">try{} catch(){}</mark>

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

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

або ось так

```javascript
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();
```

Параметр <mark style="background-color:red;">data</mark> у функції <mark style="background-color:red;">readFile</mark>, містить об'єкт типу <mark style="background-color:red;">Buffer</mark>.  Це - послідовність прочитаних байтів (тобто сирі дані).&#x20;

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

Найпростіший спосіб працювати з отриманими даними як з рядком - необхідно конвертувати <mark style="background-color:red;">data</mark> методом <mark style="background-color:red;">toString()</mark>.

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

або ось так

```javascript
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
```

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

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

Утім тут є складність в тому, що операційні системи macOS та Unix проставляють в шляхах такий слеш  <mark style="background-color:red;">/</mark>, а в операційній системі windows такий <mark style="background-color:red;">\\</mark>. Тому код шляху, який працюватиме в одні ос, не працюватиме в іншій. Для вирішення цього ньюансу є модуль <mark style="background-color:red;">path.</mark>

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

```javascript
const path = require("path");
```

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

```javascript
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
```

{% code title="files/sample.json" %}

```json
{
  "a": 123,
  "b": "Mike",
  "c": true,
  "d": "Helen"
}
```

{% endcode %}

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

{% code title="index.js" %}

```javascript
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();
```

{% endcode %}

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

&#x20;У цьому прикладі ми будемо використовувати два методи <mark style="background-color:red;">readdir</mark> (для читання директорії) і <mark style="background-color:red;">stat</mark> (для отримання статистики).

Напишімо скрипт <mark style="background-color:red;">files.js</mark>, який читатиме поточний каталог та виводити його вміст: ім'я файлу, його розмір та дату останньої зміни.

{% code title="files.js" %}

```javascript
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));
```

{% endcode %}

<mark style="background-color:red;">\_\_dirname</mark> - повертає абсолютний шлях поточного каталогу.

У наслідку виконання промісу в змінній files <mark style="background-color:red;">міститься</mark> масив імен файлів та директорій з поточної директорії. Потім ми повертаємо масив промісів з аналізом кожного елемента цього масиву.&#x20;

Провести аналіз допомагає змінна <mark style="background-color:red;">stats</mark>, де міститься  інформація про поточний файл. Тут повертається ім'я файлу, час останньої зміни <mark style="background-color:red;">stats.mtime</mark> та розмір у байтах <mark style="background-color:red;">stats.size</mark>.

Результат виконання промісу, змінна <mark style="background-color:red;">result</mark>, передається  функції <mark style="background-color:red;">console.table.</mark> В результаті на екран виводиться таблиця виконання скрипту в консолі.

Різновидом отримання статистики про файл є метод <mark style="background-color:red;">lstat</mark>.

```javascript
 fs.lstat(pathToFile);
```

Корисним методом також буде перевірка чи є елемент в директорії папкою чи файлом <mark style="background-color:red;">isDirectory</mark>.

```javascript
console.log(dirStat.isDirectory());
```

**Покликання:**&#x20;

[Node fs docs](https://nodejs.org/api/fs.html)

[Node path docs](https://nodejs.org/api/path.html)

[JSDoc](https://habr.com/ru/articles/572968/)
