Вступление
Запись в файлы - частая необходимость при программировании на любом языке. Как и другие языки программирования, JavaScript с Node.js делает работу с файловой системой интуитивно понятной благодаря использованию модуля, работающего с файловой системой операционной системы.
Модуль fs содержит функции для управления файлами и работы с файловой системой вычислительной платформы. Наряду с чтением и записью в файлы вы можете использовать модуль для запроса статистики файлов, такой как размеры и количество файлов.
По умолчанию fs
будет записывать файлы в кодировке utf8.
UTF-8 - это кодировка, обычно
используемая на веб-страницах и других документах. Кодировка файла
относится к набору символов, который используется для содержимого файла.
Обычно используются кодировки utf8, ascii, binary, hex, base64 и
utf16le.
Каждая кодировка символов имеет определенные преимущества и случаи, когда она имеет наибольший смысл. Например, ascii - это кодировка, удобная для чтения и записи, но она удаляет старший бит из потока данных. По подобным причинам вам нужно будет осторожно выбирать кодировку символов.
В следующих нескольких разделах мы представим различные способы,
которыми fs
позволяет вам писать в файловую систему, включая их
различия и преимущества.
Метод 1. Использование fs.writeFile
Модуль fs
включает высокоуровневый writeFile
который может
записывать данные в файлы асинхронно. Это означает, что, как и в случае
с множеством операций в Node.js, ввод-вывод не блокируется и генерирует
событие по завершении операции. Мы можем написать функцию обратного
вызова, которая будет запускаться при writeFile
.
Вот простой пример кода для записи текста песни в файл:
// writefile.js
const fs = require('fs');
let lyrics = 'But still I\'m having memories of high speeds when the cops crashed\n' +
'As I laugh, pushin the gas while my Glocks blast\n' +
'We was young and we was dumb but we had heart';
// write to a new file named 2pac.txt
fs.writeFile('2pac.txt', lyrics, (err) => {
// throws an error, you could also catch it here
if (err) throw err;
// success case, the file was saved
console.log('Lyric saved!');
});
Мы указали имя файла вместе с символами для записи, а также функцию обратного вызова, запускаемую при возврате метода. Мы также можем передать объект параметров или строку, которая указывает используемую кодировку, а также режим и флаг. Если вам нужно указать кодировку символов, вам нужно будет вызвать метод следующим образом:
fs.writeFile('2pac.txt', 'Some other lyric', 'ascii', callback);
Обратите внимание, что если файл еще не существует, вызов этого метода также фактически создаст файл , поэтому вам не нужно беспокоиться об этом с вашей стороны.
Существует синхронный метод fs.writeFileSync
, который можно
использовать вместо fs.writeFile
.
Разница в том, что fs.writeFileSync
выполняет операции ввода / вывода
синхронно, блокируя цикл обработки событий Node.js во время записи
файла. Это может повлиять на производительность вашего приложения
Node.js, если вы переусердствуете с синхронной записью. В большинстве
случаев вам следует предпочесть асинхронную запись.
Просто знайте, что вы должны быть осторожны при использовании этих двух
методов, потому что они будут создавать каждый раз новый файл или
заменять содержимое, если оно уже существует. Если вы просто хотите
обновить существующий файл, вы захотите использовать appendFile
,
который мы рассмотрим позже в этой статье.
Метод 2: Использование fs.write
В отличие от высокоуровневых fs.writeFile
и fs.writeFileSync
, вы
можете усилить контроль при записи в файлы в Node.js, используя
низкоуровневый метод fs.write
Метод fs.write
позволяет точно
контролировать позицию в файле, с которой начинается запись, буфер,
который вы можете указать для записи, а также то, какую часть буфера вы
хотите записать в файл.
Кроме того, использование функции записи низкого уровня позволяет вам точно знать, когда дескрипторы файлов освобождаются, и реагировать соответствующим образом, например, отправляя push-уведомления в вашем приложении.
Вот пример, в котором мы записываем еще несколько строк текста в другой
файл с помощью fs.write
.
// fs_write.js
const fs = require('fs');
// specify the path to the file, and create a buffer with characters we want to write
let path = 'ghetto_gospel.txt';
let buffer = new Buffer('Those who wish to follow me\nI welcome with my hands\nAnd the red sun sinks at last');
// open the file in writing mode, adding a callback function where we do the actual writing
fs.open(path, 'w', function(err, fd) {
if (err) {
throw 'could not open file: ' + err;
}
// write the contents of the buffer, from position 0 to the end, to the file descriptor returned in opening our file
fs.write(fd, buffer, 0, buffer.length, null, function(err) {
if (err) throw 'error writing file: ' + err;
fs.close(fd, function() {
console.log('wrote the file successfully');
});
});
});
Вы захотите использовать fs.write
при выполнении тонких обновлений
файлов, например, при записи последовательности байтов в известной
позиции в середине файла.
Эти методы работают с файловыми дескрипторами, и вы должны открыть файл
для записи с помощью fs.open
а затем, в конце, снова закрыть его с
помощью fs.close
, чтобы избежать утечки памяти.
Метод 3: Использование fs.createWriteStream
При обработке особенно больших файлов или файлов, которые поступают фрагментами, например, из сетевого подключения, использование потоков предпочтительнее записи файлов за один раз с помощью вышеуказанных методов, которые записывают файлы целиком.
Потоки записывают небольшие объемы данных за раз. Хотя это имеет тот недостаток, что он медленнее, поскольку данные передаются фрагментами, он имеет преимущества для производительности ОЗУ. Поскольку весь файл не загружается в память сразу, использование ОЗУ меньше.
Чтобы записать в файл с помощью потоков, вам необходимо создать новый доступный для записи поток. Затем вы можете записывать данные в поток через определенные промежутки времени, все сразу или в зависимости от доступности данных с сервера или другого процесса, а затем навсегда закрыть поток после того, как все пакеты данных будут записаны.
Вот пример кода того, как мы это делаем:
// write_stream.js
const fs = require('fs');
let writeStream = fs.createWriteStream('secret.txt');
// write some data with a base64 encoding
writeStream.write('aef35ghhjdk74hja83ksnfjk888sfsf', 'base64');
// the finish event is emitted when all data has been flushed from the stream
writeStream.on('finish', () => {
console.log('wrote all data to file');
});
// close the stream
writeStream.end();
Мы создали поток с возможностью записи, а затем записали в поток некоторые данные. Мы включили оператор журнала при возникновении события «finish», сообщающий нам, что все данные были сброшены в базовую систему. В этом случае это означает, что все данные были записаны в файловую систему.
Учитывая преимущества в производительности, потоки - это метод, который вы увидите широко используемым на территории Node.js, а не только для записи файлов. Другой пример - использование потоков для получения данных из сетевого подключения.
Обработка ошибок при записи в файл
При записи в файлы во время ввода / вывода может возникнуть множество различных ошибок. Ваш код должен устранять эти потенциальные ошибки. Одна простая вещь - выбросить ошибки как исключения Node.js. Это приводит к сбою программы и поэтому не рекомендуется, за исключением случаев, когда у вас мало других возможностей.
Например, если ошибка может возникать только как ошибка программиста, также известная как ошибка, то лучшим ответом может быть сбой программы, поскольку он сразу же предупреждает программиста об ошибке и не скрывает ошибку.
Когда вы имеете дело с операционными ошибками, такими как указание недоступного пути, есть два подхода. Один из них - вызвать функцию обратного вызова с ошибкой. Затем в обратном вызове вы включаете некоторую логику обработки ошибки, например ее регистрацию.
В качестве альтернативы вы можете включить блоки try / catch вокруг кода, вызывающего раздел программы, в котором могут возникнуть ошибки.
Добавление к файлу с помощью fs.appendFile
В случаях, когда мы просто хотим добавить к содержимому файла, мы можем
использовать fs.appendFile
и его синхронный аналог,
fs.appendFileSync
. Использование fs.appendFile
создает файл, если
он не существует, и добавляет к файлу в противном случае.
Вот пример использования fs.appendFile
для записи в файл.
// append_file.js
const fs = require('fs');
// add a line to a lyric file, using appendFile
fs.appendFile('empirestate.txt', '\nRight there up on Broadway', (err) => {
if (err) throw err;
console.log('The lyrics were updated!');
});
Здесь мы используем appendFile
чтобы добавить дополнительную строку к
файлу, содержащему следующие слова:
// empirestate.txt
Empire State of Mind - JAY-Z
I used to cop in Harlem;
hola, my Dominicanos
Использование fs.appendFile
не перезаписывает файл, а обновляет его с
конечной позиции, добавляя новые данные. Это полезная функция, когда вы
просто хотите обновить файл, например, постоянно добавляя данные в один
файл журнала.
Итак, после запуска нашего кода текстовый файл будет выглядеть так:
// empirestate.txt
Empire State of Mind - JAY-Z
I used to cop in Harlem;
hola, my Dominicanos
Right there up on Broadway
Как видите, старый текст все еще там, а наша новая строка добавлена в конец.
Учить больше
Хотите узнать больше об основах Node.js? Лично я бы порекомендовал пройти онлайн-курс, например Learn Node.js от Веса Боса{.bos-link} . Вы не только изучите самый современный синтаксис ES2017, но и сможете создать полноценное ресторанное приложение. По моему опыту, создание подобных реальных приложений - самый быстрый способ учиться.
Заключение
Как мы видели, существует несколько подходов, которые следует учитывать
при записи в файл в Node.js. Самый простой и часто наиболее подходящий
способ writeFile
в модуле fs
Это позволяет выполнять запись в
указанный путь к файлу с асинхронным поведением и создает новый файл или
заменяет его, если он существует по пути.
Если вы пишете файлы большего размера, вам следует учитывать возможности записи по частям для записываемых потоков. Это позволяет записывать буферы данных частями вместо того, чтобы загружать все данные сразу в память. В таких случаях использование потоков помогает достичь большей производительности.
В особых случаях вы можете использовать fs
нижнего уровня, такие как
fs.write
. Модуль fs
поддерживает мелкозернистые операции записи
файлов в соответствии с вашими конкретными потребностями.