Запись и чтение файлов. NodeJS

11.04.2024

Сегодня, в девятой части перевода руководства по Node.js, мы поговорим о работе с файлами. В частности, речь пойдёт о модулях fs и path - о файловых дескрипторах, о путях к файлам, о получении информации о файлах, об их чтении и записи, о работе с директориями.

Работа с файловыми дескрипторами в Node.js

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

Дескриптор можно получить, воспользовавшись для открытия файла асинхронным методом open() из модуля fs:

Const fs = require("fs") fs.open("/Users/flavio/test.txt", "r", (err, fd) => { //fd - это дескриптор файла })

Обратите внимание на второй параметр, r , использованный при вызове метода fs.open() . Это - флаг, который сообщает системе о том, что файл открывают для чтения. Вот ещё некоторые флаги, которые часто используются при работе с этим и некоторыми другими методами:

  • r+ - открыть файл для чтения и для записи.
  • w+ - открыть файл для чтения и для записи, установив указатель потока в начало файла. Если файл не существует - он создаётся.
  • a - открыть файл для записи, установив указатель потока в конец файла. Если файл не существует - он создаётся.
  • a+ - открыть файл для чтения и записи, установив указатель потока в конец файла. Если файл не существует - он создаётся.

Файлы можно открывать и пользуясь синхронным методом fs.openSync() , который, вместо того, чтобы предоставить дескриптор файла в коллбэке, возвращает его:

Const fs = require("fs") try { const fd = fs.openSync("/Users/flavio/test.txt", "r") } catch (err) { console.error(err) }

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

Данные о файлах

С каждым файлом связан набор данных о нём, исследовать эти данные можно средствами Node.js. В частности, сделать это можно, используя метод stat() из модуля fs .

Вызывают этот метод, передавая ему путь к файлу, и, после того, как Node.js получит необходимые сведения о файле, он вызовет коллбэк, переданный методу stat() . Вот как это выглядит:

Const fs = require("fs") fs.stat("/Users/flavio/test.txt", (err, stats) => { if (err) { console.error(err) return } //сведения о файле содержатся в аргументе `stats` })

В Node.js имеется возможность синхронного получения сведений о файлах. При таком подходе главный поток блокируется до получения свойств файла:

Const fs = require("fs") try { const stats = fs.statSync ("/Users/flavio/test.txt") } catch (err) { console.error(err) }

Информация о файле попадёт в константу stats . Что это за информация? На самом деле, соответствующий объект предоставляет нам большое количество полезных свойств и методов:

  • Методы.isFile() и.isDirectory() позволяют, соответственно, узнать, является ли исследуемый файл обычным файлом или директорией.
  • Метод.isSymbolicLink() позволяет узнать, является ли файл символической ссылкой.
  • Размер файла можно узнать, воспользовавшись свойством.size .

Тут имеются и другие методы, но эти - самые употребимые. Вот как ими пользоваться:

Const fs = require("fs") fs.stat("/Users/flavio/test.txt", (err, stats) => { if (err) { console.error(err) return } stats.isFile() //true stats.isDirectory() //false stats.isSymbolicLink() //false stats.size //1024000 //= 1MB })

Пути к файлам в Node.js и модуль path

Путь к файлу - это адрес того места в файловой системе, где он расположен.

В Linux и macOS путь может выглядеть так:

/users/flavio/file.txt

В Windows пути выглядят немного иначе:

C:usersflaviofile.txt

На различия в форматах записи путей при использовании разных операционных систем следует обращать внимание, учитывая операционную систему, используемую для развёртывания Node.js-сервера.

В Node.js есть стандартный модуль path , предназначенный для работы с путями к файлам. Перед использованием этого модуля в программе его надо подключить:

▍Получение информации о пути к файлу

Если у вас есть путь к файлу, то, используя возможности модуля path , вы можете, в удобном для восприятия и дальнейшей обработки виде, узнать подробности об этом пути. Выглядит это так:

Const notes = "/users/flavio/notes.txt" path.dirname(notes) // /users/flavio path.basename(notes) // notes.txt path.extname(notes) // .txt

Здесь, в строке notes , хранится путь к файлу. Для разбора пути использованы следующие методы модуля path:

  • dirname() - возвращает родительскую директорию файла.
  • basename() - возвращает имя файла.
  • extname() - возвращает расширение файла.

Узнать имя файла без расширения можно, вызвав метод.basename() и передав ему второй аргумент, представляющий расширение:

Path.basename(notes, path.extname(notes)) //notes

▍Работа с путями к файлам

Несколько частей пути можно объединить, используя метод path.join() :

Const name = "flavio" path.join("/", "users", name, "notes.txt") //"/users/flavio/notes.txt"

Найти абсолютный путь к файлу на основе относительного пути к нему можно с использованием метода path.resolve() :

Path.resolve("flavio.txt") //"/Users/flavio/flavio.txt" при запуске из моей домашней папки

В данном случае Node.js просто добавляет /flavio.txt к пути, ведущем к текущей рабочей директории. Если при вызове этого метода передать ещё один параметр, представляющий путь к папке, метод использует его в качестве базы для определения абсолютного пути:

Path.resolve("tmp", "flavio.txt") // "/Users/flavio/tmp/flavio.txt" при запуске из моей домашней папки

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

Path.resolve("/etc", "flavio.txt") // "/etc/flavio.txt"

Вот ещё один полезный метод - path.normalize() . Он позволяет найти реальный путь к файлу, используя путь, в котором содержатся спецификаторы относительного пути вроде точки (.), двух точек (..), или двух косых черт:

Path.normalize("/users/flavio/..//test.txt") // /users/test.txt

Методы resolve() и normalize() не проверяют существование директории. Они просто находят путь, основываясь на переданным им данным.

Чтение файлов в Node.js

Самый простой способ чтения файлов в Node.js заключается в использовании метода fs.readFile() с передачей ему пути к файлу и коллбэка, который будет вызван с передачей ему данных файла (или объекта ошибки):

Fs.readFile("/Users/flavio/test.txt", (err, data) => { if (err) { console.error(err) return } console.log(data) })

Если надо, можно воспользоваться синхронной версией этого метода - fs.readFileSync() :

Const fs = require("fs") try { const data = fs.readFileSync("/Users/flavio/test.txt") console.log(data) } catch (err) { console.error(err) }

По умолчанию при чтении файлов используется кодировка utf8 , но кодировку можно задать и самостоятельно, передав методу соответствующий параметр.

Методы fs.readFile() и fs.readFileSync() считывают в память всё содержимое файла. Это означает, что работа с большими файлами с применением этих методов серьёзно отразится на потреблении памяти вашим приложением и окажет влияние на его производительность. Если с такими файлами нужно работать, лучше всего воспользоваться потоками.

Запись файлов в Node.js

В Node.js легче всего записывать файлы с использованием метода fs.writeFile() :

Const fs = require("fs") const content = "Some content!" fs.writeFile("/Users/flavio/test.txt", content, (err) => { if (err) { console.error(err) return } //файл записан успешно })

Есть и синхронная версия того же метода - fs.writeFileSync() :

Const fs = require("fs") const content = "Some content!" try { const data = fs.writeFileSync("/Users/flavio/test.txt", content) //файл записан успешно } catch (err) { console.error(err) }

Эти методы, по умолчанию, заменяют содержимое существующих файлов. Изменить их стандартное поведение можно, воспользовавшись соответствующим флагом:

Fs.writeFile("/Users/flavio/test.txt", content, { flag: "a+" }, (err) => {})

Тут могут использоваться флаги, которые мы уже перечисляли в разделе, посвящённом дескрипторам. Подробности о флагах можно узнать .

Присоединение данных к файлу

Метод fs.appendFile() (и его синхронную версию - fs.appendFileSync()) удобно использовать для присоединения данных к концу файла:

Const content = "Some content!" fs.appendFile("file.log", content, (err) => { if (err) { console.error(err) return } //готово! })

Об использовании потоков

Выше мы описывали методы, которые, выполняя запись в файл, пишут в него весь объём переданных им данных, после чего, если используются их синхронные версии, возвращают управление программе, а если применяются асинхронные версии - вызывают коллбэки. Если вас такое состояние дел не устраивает - лучше будет воспользоваться потоками.

Работа с директориями в Node.js

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

▍Проверка существования папки

Для того чтобы проверить, существует ли директория и может ли Node.js получить к ней доступ, учитывая разрешения, можно использовать метод fs.access() .

▍Создание новой папки

Для того чтобы создавать новые папки, можно воспользоваться методами fs.mkdir() и fs.mkdirSync() :

Const fs = require("fs") const folderName = "/Users/flavio/test" try { if (!fs.existsSync(dir)){ fs.mkdirSync(dir) } } catch (err) { console.error(err) }

▍Чтение содержимого папки

Для того чтобы прочесть содержимое папки, можно воспользоваться методами fs.readdir() и fs.readdirSync() . В этом примере осуществляется чтение содержимого папки - то есть - сведений о том, какие файлы и поддиректории в ней имеются, и возврат их относительных путей:

Const fs = require("fs") const path = require("path") const folderPath = "/Users/flavio" fs.readdirSync(folderPath)

Вот так можно получить полный путь к файлу:

Fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName) }

Результаты можно отфильтровать для того, чтобы получить только файлы и исключить из вывода директории:

Const isFile = fileName => { return fs.lstatSync(fileName).isFile() } fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName)).filter(isFile) }

▍Переименование папки

Для переименования папки можно воспользоваться методами fs.rename() и fs.renameSync() . Первый параметр - это текущий путь к папке, второй - новый:

Const fs = require("fs") fs.rename("/Users/flavio", "/Users/roger", (err) => { if (err) { console.error(err) return } //готово })

Переименовать папку можно и с помощью синхронного метода fs.renameSync() :

Const fs = require("fs") try { fs.renameSync("/Users/flavio", "/Users/roger") } catch (err) { console.error(err) }

▍Удаление папки

Для того чтобы удалить папку, можно воспользоваться методами fs.rmdir() или fs.rmdirSync() . Надо отметить, что удаление папки, в которой что-то есть, задача несколько более сложная, чем удаление пустой папки. Если вам нужно удалять такие папки, воспользуйтесь пакетом fs-extra , который весьма популярен и хорошо поддерживается. Он представляет собой замену модуля fs , расширяющую его возможности.

Метод remove() из пакета fs-extra умеет удалять папки, в которых уже что-то есть.

Установить этот модуль можно так:

Npm install fs-extra

Вот пример его использования:

Const fs = require("fs-extra") const folder = "/Users/flavio" fs.remove(folder, err => { console.error(err) })

Его методами можно пользоваться в виде промисов:

Fs.remove(folder).then(() => { //готово }).catch(err => { console.error(err) })

Допустимо и применение конструкции async/await:

Async function removeFolder(folder) { try { await fs.remove(folder) //готово } catch (err) { console.error(err) } } const folder = "/Users/flavio" removeFolder(folder)

Модуль fs

Выше мы уже сталкивались с некоторыми методами модуля fs , применяемыми при работе с файловой системой. На самом деле, он содержит ещё много полезного. Напомним, что он не нуждается в установке, для того, чтобы воспользоваться им в программе, его достаточно подключить:

Const fs = require("fs")

После этого у вас будет доступ к его методам, среди которых отметим следующие, некоторые из которых вам уже знакомы:

  • fs.access() : проверяет существование файла и возможность доступа к нему с учётом разрешений.
  • fs.appendFile() : присоединяет данные к файлу. Если файл не существует - он будет создан.
  • fs.chmod() : изменяет разрешения для заданного файла. Похожие методы: fs.lchmod() , fs.fchmod() .
  • fs.chown() : изменяет владельца и группу для заданного файла. Похожие методы: fs.fchown() , fs.lchown() .
  • fs.close() : закрывает дескриптор файла.
  • fs.copyFile() : копирует файл.
  • fs.createReadStream() : создаёт поток чтения файла.
  • fs.createWriteStream() : создаёт поток записи файла.
  • fs.link() : создаёт новую жёсткую ссылку на файл.
  • fs.mkdir() : создаёт новую директорию.
  • fs.mkdtemp() : создаёт временную директорию.
  • fs.open() : открывает файл.
  • fs.readdir() : читает содержимое директории.
  • fs.readFile() : считывает содержимое файла. Похожий метод: fs.read() .
  • fs.readlink() : считывает значение символической ссылки.
  • fs.realpath() : разрешает относительный путь к файлу, построенный с использованием символов. и.. , в полный путь.
  • fs.rename() : переименовывает файл или папку.
  • fs.rmdir() : удаляет папку.
  • fs.stat() : возвращает сведения о файле. Похожие методы: fs.fstat() , fs.lstat() .
  • fs.symlink() : создаёт новую символическую ссылку на файл.
  • fs.truncate() : обрезает файл до заданной длины. Похожий метод: fs.ftruncate() .
  • fs.unlink() : удаляет файл или символическую ссылку.
  • fs.unwatchFile() : отключает наблюдение за изменениями файла.
  • fs.utimes() : изменяет временную отметку файла. Похожий метод: fs.futimes() .
  • fs.watchFile() : включает наблюдение за изменениями файла. Похожий метод: fs.watch() .
  • fs.writeFile() : записывает данные в файл. Похожий метод: fs.write() .

Интересной особенностью модуля fs является тот факт, что все его методы, по умолчанию, являются асинхронными, но существуют и их синхронные версии, имена которых получаются путём добавления слова Sync к именам асинхронных методов.

Например:

  • fs.rename()
  • fs.renameSync()
  • fs.write()
  • fs.writeSync()

Использование синхронных методов серьёзно влияет на то, как работает программа.

В Node.js 10 имеется экспериментальная поддержка этих API , основанных на промисах.

Исследуем метод fs.rename() . Вот асинхронная версия этого метода, использующая коллбэки:

Const fs = require("fs") fs.rename("before.json", "after.json", (err) => { if (err) { return console.error(err) } //готово })

При использовании его синхронной версии для обработки ошибок используется конструкция try/catch:

Const fs = require("fs") try { fs.renameSync("before.json", "after.json") //готово } catch (err) { console.error(err) }

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

Модуль path

Модуль path, о некоторых возможностях которого мы тоже уже говорили, содержит множество полезных инструментов, позволяющих взаимодействовать с файловой системой. Как уже было сказано, устанавливать его не нужно, так как он является частью Node.js. Для того чтобы пользоваться им, его достаточно подключить:

Const path = require("path")

Свойство path.sep этого модуля предоставляет символ, использующийся для разделения сегментов пути (в Windows и / в Linux и macOS), а свойство path.delimiter даёт символ, используемый для отделения друг от друга нескольких путей (; в Windows и: в Linux и macOS).

Рассмотрим и проиллюстрируем примерами некоторые методы модуля path .

▍path.basename()

Возвращает последний фрагмент пути. Передав второй параметр этому методу можно убрать расширение файла.

Require("path").basename("/test/something") //something require("path").basename("/test/something.txt") //something.txt require("path").basename("/test/something.txt", ".txt") //something

▍path.dirname()

Возвращает ту часть пути, которая представляет имя директории:

Require("path").dirname("/test/something") // /test require("path").dirname("/test/something/file.txt") // /test/something

▍path.extname()

Возвращает ту часть пути, которая представляет расширение файла: require("path").dirname("/test/something") // "" require("path").dirname("/test/something/file.txt") // ".txt"

▍path.isAbsolute()

Возвращает истинное значение если путь является абсолютным:

Require("path").isAbsolute("/test/something") // true require("path").isAbsolute("./test/something") // false

▍path.join()

Соединяет несколько частей пути:

Const name = "flavio" require("path").join("/", "users", name, "notes.txt") //"/users/flavio/notes.txt"

▍path.normalize()

Пытается выяснить реальный путь на основе пути, который содержит символы, использующиеся при построении относительных путей вроде. , .. и // :

Require("path").normalize("/users/flavio/..//test.txt") ///users/test.txt

▍path.parse()

Преобразует путь в объект, свойства которого представляют отдельные части пути:

  • root: корневая директория.
  • dir: путь к файлу, начиная от корневой директории
  • base: имя файла и расширение.
  • name: имя файла.
  • ext: расширение файла.

Вот пример использования этого метода:

Require("path").parse("/users/test.txt")

В результате его работы получается такой объект:

{ root: "/", dir: "/users", base: "test.txt", ext: ".txt", name: "test" }

▍path.relative()

Принимает, в качестве аргументов, 2 пути. Возвращает относительный путь из первого пути ко второму, основываясь на текущей рабочей директории:

Require("path").relative("/Users/flavio", "/Users/flavio/test.txt") //"test.txt" require("path").relative("/Users/flavio", "/Users/flavio/something/test.txt") //"something/test.txt"

▍path.resolve()

Находит абсолютный путь на основе переданного ему относительного пути:

Path.resolve("flavio.txt") //"/Users/flavio/flavio.txt" при запуске из моей домашней папки.

Итоги

Сегодня мы рассмотрели модули Node.js fs и path , которые используются для работы с файловой системой. В следующей части этой серии, на которой она завершается, мы обсудим модули os , events , http , поговорим о работе с потоками и с системами управления базами данных в Node.js.

Уважаемые читатели! Какими npm-пакетами вы пользуетесь при работе с файловой системой в Node.js?

  • Перевод

Сегодня, в девятой части перевода руководства по Node.js, мы поговорим о работе с файлами. В частности, речь пойдёт о модулях fs и path - о файловых дескрипторах, о путях к файлам, о получении информации о файлах, об их чтении и записи, о работе с директориями.

Работа с файловыми дескрипторами в Node.js

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

Дескриптор можно получить, воспользовавшись для открытия файла асинхронным методом open() из модуля fs:

Const fs = require("fs") fs.open("/Users/flavio/test.txt", "r", (err, fd) => { //fd - это дескриптор файла })
Обратите внимание на второй параметр, r , использованный при вызове метода fs.open() . Это - флаг, который сообщает системе о том, что файл открывают для чтения. Вот ещё некоторые флаги, которые часто используются при работе с этим и некоторыми другими методами:

  • r+ - открыть файл для чтения и для записи.
  • w+ - открыть файл для чтения и для записи, установив указатель потока в начало файла. Если файл не существует - он создаётся.
  • a - открыть файл для записи, установив указатель потока в конец файла. Если файл не существует - он создаётся.
  • a+ - открыть файл для чтения и записи, установив указатель потока в конец файла. Если файл не существует - он создаётся.
Файлы можно открывать и пользуясь синхронным методом fs.openSync() , который, вместо того, чтобы предоставить дескриптор файла в коллбэке, возвращает его:

Const fs = require("fs") try { const fd = fs.openSync("/Users/flavio/test.txt", "r") } catch (err) { console.error(err) }
После получения дескриптора любым из вышеописанных способов вы можете производить с ним необходимые операции.

Данные о файлах

С каждым файлом связан набор данных о нём, исследовать эти данные можно средствами Node.js. В частности, сделать это можно, используя метод stat() из модуля fs .

Вызывают этот метод, передавая ему путь к файлу, и, после того, как Node.js получит необходимые сведения о файле, он вызовет коллбэк, переданный методу stat() . Вот как это выглядит:

Const fs = require("fs") fs.stat("/Users/flavio/test.txt", (err, stats) => { if (err) { console.error(err) return } //сведения о файле содержатся в аргументе `stats` })
В Node.js имеется возможность синхронного получения сведений о файлах. При таком подходе главный поток блокируется до получения свойств файла:

Const fs = require("fs") try { const stats = fs.statSync ("/Users/flavio/test.txt") } catch (err) { console.error(err) }
Информация о файле попадёт в константу stats . Что это за информация? На самом деле, соответствующий объект предоставляет нам большое количество полезных свойств и методов:

  • Методы.isFile() и.isDirectory() позволяют, соответственно, узнать, является ли исследуемый файл обычным файлом или директорией.
  • Метод.isSymbolicLink() позволяет узнать, является ли файл символической ссылкой.
  • Размер файла можно узнать, воспользовавшись свойством.size .
Тут имеются и другие методы, но эти - самые употребимые. Вот как ими пользоваться:

Const fs = require("fs") fs.stat("/Users/flavio/test.txt", (err, stats) => { if (err) { console.error(err) return } stats.isFile() //true stats.isDirectory() //false stats.isSymbolicLink() //false stats.size //1024000 //= 1MB })

Пути к файлам в Node.js и модуль path

Путь к файлу - это адрес того места в файловой системе, где он расположен.

В Linux и macOS путь может выглядеть так:

/users/flavio/file.txt
В Windows пути выглядят немного иначе:

C:\users\flavio\file.txt
На различия в форматах записи путей при использовании разных операционных систем следует обращать внимание, учитывая операционную систему, используемую для развёртывания Node.js-сервера.

В Node.js есть стандартный модуль path , предназначенный для работы с путями к файлам. Перед использованием этого модуля в программе его надо подключить:

▍Получение информации о пути к файлу

Если у вас есть путь к файлу, то, используя возможности модуля path , вы можете, в удобном для восприятия и дальнейшей обработки виде, узнать подробности об этом пути. Выглядит это так:

Const notes = "/users/flavio/notes.txt" path.dirname(notes) // /users/flavio path.basename(notes) // notes.txt path.extname(notes) // .txt
Здесь, в строке notes , хранится путь к файлу. Для разбора пути использованы следующие методы модуля path:

  • dirname() - возвращает родительскую директорию файла.
  • basename() - возвращает имя файла.
  • extname() - возвращает расширение файла.
Узнать имя файла без расширения можно, вызвав метод.basename() и передав ему второй аргумент, представляющий расширение:

Path.basename(notes, path.extname(notes)) //notes

▍Работа с путями к файлам

Несколько частей пути можно объединить, используя метод path.join() :

Const name = "flavio" path.join("/", "users", name, "notes.txt") //"/users/flavio/notes.txt"
Найти абсолютный путь к файлу на основе относительного пути к нему можно с использованием метода path.resolve() :

Path.resolve("flavio.txt") //"/Users/flavio/flavio.txt" при запуске из моей домашней папки
В данном случае Node.js просто добавляет /flavio.txt к пути, ведущем к текущей рабочей директории. Если при вызове этого метода передать ещё один параметр, представляющий путь к папке, метод использует его в качестве базы для определения абсолютного пути:

Path.resolve("tmp", "flavio.txt") // "/Users/flavio/tmp/flavio.txt" при запуске из моей домашней папки
Если путь, переданный в качестве первого параметра, начинается с косой черты - это означает, что он представляет собой абсолютный путь.

Path.resolve("/etc", "flavio.txt") // "/etc/flavio.txt"
Вот ещё один полезный метод - path.normalize() . Он позволяет найти реальный путь к файлу, используя путь, в котором содержатся спецификаторы относительного пути вроде точки (.), двух точек (..), или двух косых черт:

Path.normalize("/users/flavio/..//test.txt") // /users/test.txt
Методы resolve() и normalize() не проверяют существование директории. Они просто находят путь, основываясь на переданным им данным.

Чтение файлов в Node.js

Самый простой способ чтения файлов в Node.js заключается в использовании метода fs.readFile() с передачей ему пути к файлу и коллбэка, который будет вызван с передачей ему данных файла (или объекта ошибки):

Fs.readFile("/Users/flavio/test.txt", (err, data) => { if (err) { console.error(err) return } console.log(data) })
Если надо, можно воспользоваться синхронной версией этого метода - fs.readFileSync() :

Const fs = require("fs") try { const data = fs.readFileSync("/Users/flavio/test.txt") console.log(data) } catch (err) { console.error(err) }
По умолчанию при чтении файлов используется кодировка utf8 , но кодировку можно задать и самостоятельно, передав методу соответствующий параметр.

Методы fs.readFile() и fs.readFileSync() считывают в память всё содержимое файла. Это означает, что работа с большими файлами с применением этих методов серьёзно отразится на потреблении памяти вашим приложением и окажет влияние на его производительность. Если с такими файлами нужно работать, лучше всего воспользоваться потоками.

Запись файлов в Node.js

В Node.js легче всего записывать файлы с использованием метода fs.writeFile() :

Const fs = require("fs") const content = "Some content!" fs.writeFile("/Users/flavio/test.txt", content, (err) => { if (err) { console.error(err) return } //файл записан успешно })
Есть и синхронная версия того же метода - fs.writeFileSync() :

Const fs = require("fs") const content = "Some content!" try { const data = fs.writeFileSync("/Users/flavio/test.txt", content) //файл записан успешно } catch (err) { console.error(err) }
Эти методы, по умолчанию, заменяют содержимое существующих файлов. Изменить их стандартное поведение можно, воспользовавшись соответствующим флагом:

Fs.writeFile("/Users/flavio/test.txt", content, { flag: "a+" }, (err) => {})
Тут могут использоваться флаги, которые мы уже перечисляли в разделе, посвящённом дескрипторам. Подробности о флагах можно узнать .

Присоединение данных к файлу

Метод fs.appendFile() (и его синхронную версию - fs.appendFileSync()) удобно использовать для присоединения данных к концу файла:

Const content = "Some content!" fs.appendFile("file.log", content, (err) => { if (err) { console.error(err) return } //готово! })

Об использовании потоков

Выше мы описывали методы, которые, выполняя запись в файл, пишут в него весь объём переданных им данных, после чего, если используются их синхронные версии, возвращают управление программе, а если применяются асинхронные версии - вызывают коллбэки. Если вас такое состояние дел не устраивает - лучше будет воспользоваться потоками.

Работа с директориями в Node.js

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

▍Проверка существования папки

Для того чтобы проверить, существует ли директория и может ли Node.js получить к ней доступ, учитывая разрешения, можно использовать метод fs.access() .

▍Создание новой папки

Для того чтобы создавать новые папки, можно воспользоваться методами fs.mkdir() и fs.mkdirSync() :

Const fs = require("fs") const folderName = "/Users/flavio/test" try { if (!fs.existsSync(dir)){ fs.mkdirSync(dir) } } catch (err) { console.error(err) }

▍Чтение содержимого папки

Для того чтобы прочесть содержимое папки, можно воспользоваться методами fs.readdir() и fs.readdirSync() . В этом примере осуществляется чтение содержимого папки - то есть - сведений о том, какие файлы и поддиректории в ней имеются, и возврат их относительных путей:

Const fs = require("fs") const path = require("path") const folderPath = "/Users/flavio" fs.readdirSync(folderPath)
Вот так можно получить полный путь к файлу:

Fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName) }
Результаты можно отфильтровать для того, чтобы получить только файлы и исключить из вывода директории:

Const isFile = fileName => { return fs.lstatSync(fileName).isFile() } fs.readdirSync(folderPath).map(fileName => { return path.join(folderPath, fileName)).filter(isFile) }

▍Переименование папки

Для переименования папки можно воспользоваться методами fs.rename() и fs.renameSync() . Первый параметр - это текущий путь к папке, второй - новый:

Const fs = require("fs") fs.rename("/Users/flavio", "/Users/roger", (err) => { if (err) { console.error(err) return } //готово })
Переименовать папку можно и с помощью синхронного метода fs.renameSync() :

Const fs = require("fs") try { fs.renameSync("/Users/flavio", "/Users/roger") } catch (err) { console.error(err) }

▍Удаление папки

Для того чтобы удалить папку, можно воспользоваться методами fs.rmdir() или fs.rmdirSync() . Надо отметить, что удаление папки, в которой что-то есть, задача несколько более сложная, чем удаление пустой папки. Если вам нужно удалять такие папки, воспользуйтесь пакетом fs-extra , который весьма популярен и хорошо поддерживается. Он представляет собой замену модуля fs , расширяющую его возможности.

Метод remove() из пакета fs-extra умеет удалять папки, в которых уже что-то есть.

Установить этот модуль можно так:

Npm install fs-extra
Вот пример его использования:

Const fs = require("fs-extra") const folder = "/Users/flavio" fs.remove(folder, err => { console.error(err) })
Его методами можно пользоваться в виде промисов:

Fs.remove(folder).then(() => { //готово }).catch(err => { console.error(err) })
Допустимо и применение конструкции async/await:

Async function removeFolder(folder) { try { await fs.remove(folder) //готово } catch (err) { console.error(err) } } const folder = "/Users/flavio" removeFolder(folder)

Модуль fs

Выше мы уже сталкивались с некоторыми методами модуля fs , применяемыми при работе с файловой системой. На самом деле, он содержит ещё много полезного. Напомним, что он не нуждается в установке, для того, чтобы воспользоваться им в программе, его достаточно подключить:

Const fs = require("fs")
После этого у вас будет доступ к его методам, среди которых отметим следующие, некоторые из которых вам уже знакомы:

  • fs.access() : проверяет существование файла и возможность доступа к нему с учётом разрешений.
  • fs.appendFile() : присоединяет данные к файлу. Если файл не существует - он будет создан.
  • fs.chmod() : изменяет разрешения для заданного файла. Похожие методы: fs.lchmod() , fs.fchmod() .
  • fs.chown() : изменяет владельца и группу для заданного файла. Похожие методы: fs.fchown() , fs.lchown() .
  • fs.close() : закрывает дескриптор файла.
  • fs.copyFile() : копирует файл.
  • fs.createReadStream() : создаёт поток чтения файла.
  • fs.createWriteStream() : создаёт поток записи файла.
  • fs.link() : создаёт новую жёсткую ссылку на файл.
  • fs.mkdir() : создаёт новую директорию.
  • fs.mkdtemp() : создаёт временную директорию.
  • fs.open() : открывает файл.
  • fs.readdir() : читает содержимое директории.
  • fs.readFile() : считывает содержимое файла. Похожий метод: fs.read() .
  • fs.readlink() : считывает значение символической ссылки.
  • fs.realpath() : разрешает относительный путь к файлу, построенный с использованием символов. и.. , в полный путь.
  • fs.rename() : переименовывает файл или папку.
  • fs.rmdir() : удаляет папку.
  • fs.stat() : возвращает сведения о файле. Похожие методы: fs.fstat() , fs.lstat() .
  • fs.symlink() : создаёт новую символическую ссылку на файл.
  • fs.truncate() : обрезает файл до заданной длины. Похожий метод: fs.ftruncate() .
  • fs.unlink() : удаляет файл или символическую ссылку.
  • fs.unwatchFile() : отключает наблюдение за изменениями файла.
  • fs.utimes() : изменяет временную отметку файла. Похожий метод: fs.futimes() .
  • fs.watchFile() : включает наблюдение за изменениями файла. Похожий метод: fs.watch() .
  • fs.writeFile() : записывает данные в файл. Похожий метод: fs.write() .
Интересной особенностью модуля fs является тот факт, что все его методы, по умолчанию, являются асинхронными, но существуют и их синхронные версии, имена которых получаются путём добавления слова Sync к именам асинхронных методов.

Например:

  • fs.rename()
  • fs.renameSync()
  • fs.write()
  • fs.writeSync()
Использование синхронных методов серьёзно влияет на то, как работает программа.

В Node.js 10 имеется экспериментальная поддержка этих API , основанных на промисах.

Исследуем метод fs.rename() . Вот асинхронная версия этого метода, использующая коллбэки:

Const fs = require("fs") fs.rename("before.json", "after.json", (err) => { if (err) { return console.error(err) } //готово })
При использовании его синхронной версии для обработки ошибок используется конструкция try/catch:

Const fs = require("fs") try { fs.renameSync("before.json", "after.json") //готово } catch (err) { console.error(err) }
Основное различие между этими вариантами использования данного метода заключается в том, что во втором случае выполнение скрипта будет заблокировано до завершения файловой операции.

Модуль path

Модуль path, о некоторых возможностях которого мы тоже уже говорили, содержит множество полезных инструментов, позволяющих взаимодействовать с файловой системой. Как уже было сказано, устанавливать его не нужно, так как он является частью Node.js. Для того чтобы пользоваться им, его достаточно подключить:

Const path = require("path")
Свойство path.sep этого модуля предоставляет символ, использующийся для разделения сегментов пути (\ в Windows и / в Linux и macOS), а свойство path.delimiter даёт символ, используемый для отделения друг от друга нескольких путей (; в Windows и: в Linux и macOS).

Рассмотрим и проиллюстрируем примерами некоторые методы модуля path .

▍path.basename()

Возвращает последний фрагмент пути. Передав второй параметр этому методу можно убрать расширение файла.

Require("path").basename("/test/something") //something require("path").basename("/test/something.txt") //something.txt require("path").basename("/test/something.txt", ".txt") //something

▍path.dirname()

Возвращает ту часть пути, которая представляет имя директории:

Require("path").dirname("/test/something") // /test require("path").dirname("/test/something/file.txt") // /test/something

▍path.extname()

Возвращает ту часть пути, которая представляет расширение файла:

Require("path").extname("/test/something") // "" require("path").extname("/test/something/file.txt") // ".txt"

▍path.isAbsolute()

Возвращает истинное значение если путь является абсолютным:

Require("path").isAbsolute("/test/something") // true require("path").isAbsolute("./test/something") // false

▍path.join()

Соединяет несколько частей пути:

Const name = "flavio" require("path").join("/", "users", name, "notes.txt") //"/users/flavio/notes.txt"

▍path.normalize()

Пытается выяснить реальный путь на основе пути, который содержит символы, использующиеся при построении относительных путей вроде. , .. и // :

Require("path").normalize("/users/flavio/..//test.txt") ///users/test.txt

▍path.parse()

Преобразует путь в объект, свойства которого представляют отдельные части пути:
  • root: корневая директория.
  • dir: путь к файлу, начиная от корневой директории
  • base: имя файла и расширение.
  • name: имя файла.
  • ext: расширение файла.
Вот пример использования этого метода:

Require("path").parse("/users/test.txt")
В результате его работы получается такой объект:

{ root: "/", dir: "/users", base: "test.txt", ext: ".txt", name: "test" }

▍path.relative()

Принимает, в качестве аргументов, 2 пути. Возвращает относительный путь из первого пути ко второму, основываясь на текущей рабочей директории:

Require("path").relative("/Users/flavio", "/Users/flavio/test.txt") //"test.txt" require("path").relative("/Users/flavio", "/Users/flavio/something/test.txt") //"something/test.txt"

▍path.resolve()

Находит абсолютный путь на основе переданного ему относительного пути:

Path.resolve("flavio.txt") //"/Users/flavio/flavio.txt" при запуске из моей домашней папки.

Итоги

Сегодня мы рассмотрели модули Node.js fs и path , которые используются для работы с файловой системой. В следующей части этой серии, на которой она завершается, мы обсудим модули os , events , http , поговорим о работе с потоками и с системами управления базами данных в Node.js.

Уважаемые читатели! Какими npm-пакетами вы пользуетесь при работе с файловой системой в Node.js?

Всем привет! В этой статье мы рассмотрим, как записывать и читать файлы в NodeJS.

Для этого нам потребуется использовать модуль FS(file system). var fs =require(`fs`)-

Для демонстрации считывания содержимого файлов давайте создадим файлик с названием readme.txt.

Видео: node.js » модуль fs » запись в файл

// содержимое файла readme.txt
Здеськакое-нибудьсодержимоефайла

var text = fs.readFileSync(`readme.txt`,`utf8`)-
console.log(text)-

Мы используем метод, в который передаем первым параметром имя файла, а вторым – кодировку. Как понятно из названия, этот метод является синхронным. Это значит, что весь код, который идет ниже, выполнится только тогда, когда весь файл будет прочитан. Дальше мы просто записываем полученные данные в переменную, которую потом выводим на экран.

Видео: Node.js — создание Excel файла по шаблону

Теперь давайте попробуем считанное нами содержание файла записать в новый файл.

Для этого напишем следующее:

fs.writeFileSync(`writeme.txt`, text)-

Теперь после запуска кода вы увидите, что создался новый файлик с названием writeme.txt, в котором будет содержимое, записанное в переменную text из файла readme.txt.

Видео: Основы Node.JS. 2. Работа с файлами

Давайте рассмотрим, как использовать методы асинхронно.

Например, считаем файлик readme.txt:


console.log(data)-
})-

console.log(`выведется раньше, чем данные из файла`)-

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

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

Видео: Работа с файлами, модуль fs

Теперь давайте снова считаем содержимое файла readme.txt и запишем его в файл writeme.txt, но только теперь асинхронно.

fs.readFile(`readme.txt`,`utf8`,function(error, data){
fs.writeFile(`writeme.txt`, data)-
})-

А на этом у меня сегодня все. Спасибо за внимание!

(Пока оценок нет)

Если вы хотите сделать веб-сервер на Node.js или просто какое-то приложение, тогда вам нужно уметь читать файлы.

Node предоставляет библиотеку для работы с файловой системой. К примеру, для чтения файлов.

Асинхронное чтение файла (неблокирующее)

"Нормальный" способ чтения файлов в Node.js это чтение асинхронным способом. Это значит, что вы вызываете команду чтения файла и передаете callback, который будет вызван при завершении чтения. Это позволяет работать с несколькими запросами чтения параллельно.

Для этого мы можем использовать метод readFile из класса fs .

examples/node/non-blocking-read-file.js

Var fs = require("fs"); fs.readFile("DATA", "utf8", function(err, contents) { console.log(contents); }); console.log("after calling readFile");

Для начала мы загружаем класс fs с помощью команды require . Затем вызываем метод readFile , который получает 3 параметра: имя файла ("DATA" в нашем случае), кодировку файла ("utf8" в примере) и функцию. Эта функция будет вызывана, когда завершится операция чтения файла. Функция получит два параметра. Первый - информация о каких-либо ошибках, второй - содержимое файла.

Как только программа будет запущена, Node начнет читать файл в фоновом режиме, но продолжит выполнение. Таким образом, сначала будет выполнен вызов console.log("after calling readFile"); , который выведет этот текст в консоль. Затем, когда содержимое файла будет загружено в память, Node вызовет функцию, которую мы передали в метод readFile , и она выведет в консоль содержимое файла.

Синхронное чтение файла (блокирующее)

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

Для чтения файлов вы можете использовать метод readFileSync из класса fs :

examples/node/blocking-read-file.js

Var fs = require("fs"); var contents = fs.readFileSync("DATA", "utf8"); console.log(contents);

Последнее обновление: 23.05.2019

Для работы с файлами в Node.js предназначен модуль . Рассмотрим, как с ним работать.

Чтение из файла

Допустим, в одной папке с файлом приложения app.js расположен текстовый файл hello.txt с простейшим текстом, например:

Hello Node JS!

Для чтения файла в синхронном варианте применяется функция fs.readFileSync() :

Let fileContent = fs.readFileSync("hello.txt", "utf8");

В метод передается путь к файлу относительно файла приложения app.js, а в качестве второго параметра указывается кодировка для получения текстового содержимого файла. На выходе получаем считанный текст.

Для асинхронного чтения файла применяется функция fs.readFile :

Fs.readFile("hello.txt", "utf8", function(error,data){ });

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

Для чтения файла определим в файле app.js следующий код:

Const fs = require("fs"); // асинхронное чтение fs.readFile("hello.txt", "utf8", function(error,data){ console.log("Асинхронное чтение файла"); if(error) throw error; // если возникла ошибка console.log(data); // выводим считанные данные }); // синхронное чтение console.log("Синхронное чтение файла") let fileContent = fs.readFileSync("hello.txt", "utf8"); console.log(fileContent);

И здесь стоит обратить внимание, что несмотря на то, что функция fs.readFile() вызывается первой, но так как она асинхронная, она не блокирует поток выполнения, поэтому ее результат выводится в самом конце.

Запись файла

Для записи файла в синхронном варианте используется функция fs.writeFileSync() , которая в качестве параметра принимает путь к файлу и записываемые данные:

Fs.writeFileSync("hello.txt", "Привет ми ми ми!")

Также для записи файла можно использовать асинхронную функцию fs.writeFile() , которая принимает те же параметры:

Fs.writeFile("hello.txt", "Привет МИГ-29!")

В качестве вспомогательного параметра в функцию может передаваться функция обратного вызова, которая выполняется после завершения записи:

Const fs = require("fs"); fs.writeFile("hello.txt", "Hello мир!", function(error){ if(error) throw error; // если возникла ошибка console.log("Асинхронная запись файла завершена. Содержимое файла:"); let data = fs.readFileSync("hello.txt", "utf8"); console.log(data); // выводим считанные данные });

Следует отметить, что эти методы полностью перезаписывают файл. Если надо дозаписать файл, то применяются методы fs.appendFile()/fs.appendFileSync() :

Const fs = require("fs"); fs.appendFileSync("hello.txt", "Привет ми ми ми!"); fs.appendFile("hello.txt", "Привет МИД!", function(error){ if(error) throw error; // если возникла ошибка console.log("Запись файла завершена. Содержимое файла:"); let data = fs.readFileSync("hello.txt", "utf8"); console.log(data); // выводим считанные данные });

Удаление файла

Для удаления файла в синхронном варианте используется функция fs.unlinkSync() , которая в качестве параметра принимает путь к удаляемому файлу:

Fs.unlinkSync("hello.txt")

Также для удаления файла можно использовать асинхронную функцию fs.unlink() , которая принимает путь к файлу и функцию, вызываемую при завершении удаления:

Fs.unlink("hello.txt", (err) => { if (err) console.log(err); // если возникла ошибка else console.log("hello.txt was deleted"); });