В этой статье мы рассмотрим основной модуль файловой системы, файловые потоки и некоторые альтернативы модуля fs.

Представьте, что у вас есть задача, которую нужно выполнить с помощью Node.js ..

Проблема, с которой вы сталкиваетесь, кажется простой, но вместо того, чтобы проверять официальную документацию Node.js, вы отправляетесь в Google или npm и ищете модуль, который может сделать эту работу за вас.

Хотя это совершенно нормально; иногда основные модули могут легко помочь вам.

В этой новой серии Освоение основных модулей Node.js вы узнаете, какие скрытые / малоизвестные функции имеют основные модули и как их можно использовать. Мы также упомянем модули, которые расширяют свое поведение и являются отличным дополнением к вашему ежедневному процессу разработки.

Модуль Node.js fs

Файловый ввод-вывод обеспечивается простыми оболочками для стандартных функций POSIX. Чтобы использовать модуль fs, вы должны потребовать его с require('fs'). Все методы имеют асинхронную и синхронную формы.

Асинхронный API

// the async api 
const fs = require('fs') 
fs.unlink('/tmp/hello', (err) => {
  if (err) {
    return console.log(err)
  } 
  console.log('successfully deleted /tmp/hello') 
})

При разработке производственного кода всегда следует использовать асинхронный API, поскольку он не блокирует цикл событий, чтобы вы могли создавать высокопроизводительные приложения.

Синхронный API

// the sync api 
const fs = require('fs') 
try { 
  fs.unlinkSync('/tmp/hello')
} catch (ex) {
  console.log(ex) 
} 
console.log('successfully deleted /tmp/hello');

Вы должны использовать синхронный API только при создании проверочных концептуальных приложений или небольших интерфейсов командной строки.

Файловые потоки Node.js

Мы часто видим, что разработчики практически не используют файловые потоки.

Потоки в @nodejs - это мощные концепции - с их помощью вы можете добиться небольшого объема памяти, занимаемой вашими приложениями.

Что такое потоки Node.js?

Потоки - это первоклассная конструкция в Node.js для обработки данных. Необходимо понять три основных понятия:

  • источник - объект, откуда берутся ваши данные,
  • конвейер - где ваши данные проходят через (вы можете отфильтровать или изменить его здесь),
  • раковина - там, где заканчиваются ваши данные.

Для получения дополнительной информации см. Руководство по потокам Substack .

Поскольку основной модуль fs не предоставляет возможности для копирования файлов, вы можете легко сделать это с помощью потоков:

// copy a file 
const fs = require('fs') 
const readableStream = fs.createReadStream('original.txt') 
var writableStream = fs.createWriteStream('copy.txt') 
readableStream.pipe(writableStream)

Вы можете спросить - зачем мне это делать, если это всего лишь cp команда?

Самым большим преимуществом использования потоков в этом случае является возможность преобразовывать файлы - вы можете легко сделать что-то вроде этого, чтобы распаковать файл:

const fs = require('fs') 
const zlib = require('zlib') 
fs.createReadStream('original.txt.gz') 
  .pipe(zlib.createGunzip())   
  .pipe(fs.createWriteStream('original.txt'))

Когда не использовать fs.access

Цель метода fs.access - проверить, есть ли у пользователя разрешения для данного файла или пути, примерно так:

fs.access('/etc/passwd', fs.constants.R_OK | 
fs.constants.W_OK, (err) => { 
  if (err) { 
    return console.error('no access')
  } 
  console.log('access for read/write') 
})

Константы, доступные для проверки разрешений:

  • fs.constants.F_OK - чтобы проверить, виден ли путь вызывающему процессу,
  • fs.constants.R_OK - чтобы проверить, может ли путь быть прочитан процессом,
  • fs.constants.W_OK - чтобы проверить, может ли процесс быть записан,
  • fs.constants.X_OK - чтобы проверить, может ли этот путь быть выполнен процессом.

Однако обратите внимание, что использовать fs.access для проверки доступности файла перед вызовом fs.open, fs.readFile или fs.writeFile не рекомендуется.

Причина проста - если вы это сделаете, вы создадите состояние гонки. Между вашей проверкой и фактической операцией с файлом другой процесс мог уже изменить этот файл.

Вместо этого вам следует открыть файл напрямую и обработать там ошибки.

Предостережения относительно fs.watch

С помощью метода fs.watch вы можете отслеживать изменения файла или каталога.

Однако fs.watch API не на 100% согласован для разных платформ, а в некоторых системах он вообще недоступен:

  • в системах Linux используется inotify,
  • в системах BSD используется kqueue,
  • в OS X используется kqueue для файлов и FSEvents для каталогов,
  • в системах SunOS (включая Solaris и SmartOS) здесь используются порты событий,
  • в системах Windows эта функция зависит от ReadDirectoryChangesW.

Обратите внимание, что рекурсивный вариант поддерживается только в OS X и Windows, но не в Linux.

Кроме того, аргумент fileName в обратном вызове watch не всегда предоставляется (поскольку он поддерживается только в Linux и Windows), поэтому вам следует подготовиться к откату, если он равен undefined:

fs.watch('some/path', (eventType, fileName) => { 
  if (!filename) { 
    //filename is missing, handle it gracefully 
  } 
})

Полезные fs модули от npm

Сообщество поддерживает несколько очень полезных модулей, расширяющих функциональность модуля fs.

graceful-fs

graceful-fs является заменой основного модуля fs с некоторыми улучшениями:

  • ставит в очередь вызовы open и readdir и повторяет их, когда что-то закрывается, если есть ошибка EMFILE из-за слишком большого количества файловых дескрипторов,
  • игнорирует ошибки EINVAL и EPERM в chown, fchown или lchown, если пользователь не является пользователем root,
  • делает lchmod и lchown нупами, если они недоступны,
  • пытается прочитать файл, если чтение приводит к EAGAIN ошибке.

Вы можете начать использовать его так же, как основной модуль fs, или, альтернативно, исправив глобальный модуль.

// use as a standalone module 
const fs = require('graceful-fs') 

// patching the global one 
const originalFs = require('fs') 
const gracefulFs = require('graceful-fs') gracefulFs.gracefulify(originalFs)

mock-fs

Модуль mock-fs позволяет временно поддерживать встроенный в Node модуль fs с помощью фиктивной файловой системы в памяти. Это позволяет запускать тесты для набора фиктивных файлов или каталогов.

Начать пользоваться модулем очень просто:

const mock = require('mock-fs') 
const fs = require('fs') 
mock({
  'path/to/fake/dir': {
    'some-file.txt': 'file content here', 
    'empty-dir': {}
  }, 
  'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9])
}) 
fs.exists('path/to/fake/dir', function (exists) {   
  console.log(exists) 
  // will output true })

lockfile

Блокировка файла - это способ ограничить доступ к файлу, разрешив доступ только одному процессу в любой момент времени. Это может предотвратить сценарии состояния гонки.

Добавление файлов блокировки с помощью модуля lockfile несложно:

const lockFile = require('lockfile')
lockFile.lock('some-file.lock', function (err) { 
  // if the err happens, then it failed to acquire a lock. 
  // if there was not an error, then the file was created, 
  // and won't be deleted until we unlock it. 
  // then, some time later, do: 
  lockFile.unlock('some-file.lock', function (err) { 
  }) 
})

Заключение

Я надеюсь, что это было полезным объяснением файловой системы Node.js и ее возможностей.

Если у вас есть какие-либо вопросы по теме, дайте мне знать в разделе комментариев ниже.

Первоначально опубликовано на blog.risingstack.com 2 мая 2017 г.