Сегодня, в девятой части перевода руководства по Node.js, мы поговорим о работе с файлами. В частности, речь пойдёт о модулях fs и path — о файловых дескрипторах, о путях к файлам, о получении информации о файлах, об их чтении и записи, о работе с директориями.
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)
}
stat()
из модуля fs
.stat()
. Вот как это выглядит:const fs = require('fs')
fs.stat('/Users/flavio/test.txt', (err, stats) => {
if (err) {
console.error(err)
return
}
//сведения о файле содержатся в аргументе `stats`
})
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
})
/users/flavio/file.txt
C:\users\flavio\file.txt
path
, предназначенный для работы с путями к файлам. Перед использованием этого модуля в программе его надо подключить:const path = require('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' при запуске из моей домашней папки
/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()
не проверяют существование директории. Они просто находят путь, основываясь на переданным им данным.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()
считывают в память всё содержимое файла. Это означает, что работа с большими файлами с применением этих методов серьёзно отразится на потреблении памяти вашим приложением и окажет влияние на его производительность. Если с такими файлами нужно работать, лучше всего воспользоваться потоками.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
}
//готово!
})
fs
предоставляет в распоряжение разработчика много удобных методов, которые можно использовать для работы с директориями.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 function removeFolder(folder) {
try {
await fs.remove(folder)
//готово
} catch (err) {
console.error(err)
}
}
const folder = '/Users/flavio'
removeFolder(folder)
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()
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)
}
const path = require('path')
path.sep
этого модуля предоставляет символ, использующийся для разделения сегментов пути (\
в Windows и /
в Linux и macOS), а свойство path.delimiter
даёт символ, используемый для отделения друг от друга нескольких путей (;
в Windows и :
в Linux и macOS).path
.require('path').basename('/test/something') //something
require('path').basename('/test/something.txt') //something.txt
require('path').basename('/test/something.txt', '.txt') //something
require('path').dirname('/test/something') // /test
require('path').dirname('/test/something/file.txt') // /test/something
require('path').extname('/test/something') // ''
require('path').extname('/test/something/file.txt') // '.txt'
require('path').isAbsolute('/test/something') // true
require('path').isAbsolute('./test/something') // false
const name = 'flavio'
require('path').join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt'
.
, ..
и //
:require('path').normalize('/users/flavio/..//test.txt') ///users/test.txt
root
: корневая директория.dir
: путь к файлу, начиная от корневой директорииbase
: имя файла и расширение.name
: имя файла.ext
: расширение файла.require('path').parse('/users/test.txt')
{
root: '/',
dir: '/users',
base: 'test.txt',
ext: '.txt',
name: 'test'
}
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('flavio.txt')
//'/Users/flavio/flavio.txt' при запуске из моей домашней папки.
fs
и path
, которые используются для работы с файловой системой. В следующей части этой серии, на которой она завершается, мы обсудим модули os
, events
, http
, поговорим о работе с потоками и с системами управления базами данных в Node.js.К сожалению, не доступен сервер mySQL