Node js файлы в папке. NodeJS
Всем привет! В этой статье мы рассмотрим, как записывать и читать файлы в NodeJS .
Платформа NodeJS позволяет записывать и читать файлы в операционной системе. Для этого нам потребуется использовать модуль FS (file system ).
Var fs = require("fs");
Для демонстрации считывания содержимого файлов давайте создадим файлик с названием readme.txt .
// содержимое файла readme.txt
Здесь какое-нибудь содержимое файла
Var text = fs.readFileSync("readme.txt", "utf8");
console.log(text);
Мы используем метод, в который передаем первым параметром имя файла, а вторым – кодировку. Как понятно из названия, этот метод является синхронным. Это значит, что весь код, который идет ниже, выполнится только тогда, когда весь файл будет прочитан. Дальше мы просто записываем полученные данные в переменную, которую потом выводим на экран.
Теперь давайте попробуем считанное нами содержание файла записать в новый файл. Для этого напишем следующее:
Fs.writeFileSync("writeme.txt", text);
Теперь после запуска кода вы увидите, что создался новый файлик с названием writeme.txt , в котором будет содержимое, записанное в переменную text из файла readme.txt .
Давайте рассмотрим, как использовать методы асинхронно. Например, считаем файлик readme.txt :
console.log(data);
});
Console.log("выведется раньше, чем данные из файла");
Использование почти такое же, но теперь мы также третьим параметром передаем функцию, где в качестве аргументов первым идет ошибка, а вторым содержимое файла, которое мы потом и выводим. Ниже я написал еще один вывод текста, чтобы показать вам, что метод действительно асинхронный, поэтому, пока идет считывание файла, выполнится код ниже, а только потом выведется текст из файлика.
Теперь давайте снова считаем содержимое файла readme.txt и запишем его в файл writeme.txt , но только теперь асинхронно.
Fs.readFile("readme.txt", "utf8", function(error, data) {
fs.writeFile("writeme.txt", data);
});
А на этом у меня сегодня все. Спасибо за внимание!
Последнее обновление: 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"); });
Сегодня, в девятой части перевода руководства по 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