Как добавить данные в файл в Node?
Я пытаюсь добавить строку в файл журнала. Однако функция writeFile каждый раз стирает содержимое файла перед записью новой строки.
fs.writeFile('log.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('Сохранено!');
}); // => log.txt очищен, содержит только 'Hello Node'
Есть ли у кого-нибудь идеи, как сделать это проще?
5 ответ(ов)
Когда вы хотите записывать данные в лог-файл, т.е. добавлять информацию в конец файла, никогда не используйте appendFile
. Функция appendFile
открывает дескриптор файла для каждой записи, которую вы добавляете в файл, и после некоторого времени у вас возникнет красивая ошибка EMFILE
.
Также хочу отметить, что appendFile
не проще в использовании, чем WriteStream
.
Пример с использованием appendFile
:
console.log(new Date().toISOString());
[...Array(10000)].forEach(function (item, index) {
fs.appendFile("append.txt", index + "\n", function (err) {
if (err) console.log(err);
});
});
console.log(new Date().toISOString());
На моем компьютере можно добавлять данные в файл до 8000 раз, а затем возникает следующая ошибка:
{ Error: EMFILE: too many open files, open 'C:\mypath\append.txt'
at Error (native)
errno: -4066,
code: 'EMFILE',
syscall: 'open',
path: 'C:\\mypath\\append.txt' }
Кроме того, appendFile
будет записывать данные по мере своего выполнения, поэтому ваши логи не будут записываться с отметкой времени. Вы можете протестировать это, установив 1000 вместо 100000 - порядок записи будет случайным, в зависимости от доступа к файлу.
Если вы хотите добавлять данные в файл, вы должны использовать доступный поток, вот так:
var stream = fs.createWriteStream("append.txt", { flags: 'a' });
console.log(new Date().toISOString());
[...Array(10000)].forEach(function (item, index) {
stream.write(index + "\n");
});
console.log(new Date().toISOString());
stream.end();
Вы завершаете его, когда хотите. Вам даже не обязательно использовать stream.end()
, поскольку по умолчанию стоит опция AutoClose: true
, и ваш файл будет закрыт, когда завершится ваш процесс, что позволяет избежать открытия слишком большого количества файлов.
Ваш код, использующий createWriteStream
, создает файловый дескриптор для каждой записи. Однако использование logStream.end()
предпочтительнее, поскольку оно сообщает Node.js закрыть поток сразу после завершения записи.
Вот пример кода:
var fs = require('fs');
var logStream = fs.createWriteStream('log.txt', {flags: 'a'});
// Используйте {flags: 'a'} для добавления и {flags: 'w'} для перезаписи файла
logStream.write('Начальная строка...');
logStream.end('Это заключительная строка');
Таким образом, logStream.end()
гарантирует, что поток завершится корректно и файл будет закрыт, что может помочь избежать утечек ресурсов.
Чтобы использовать флаги a+
для добавления и создания файла (если он не существует), вы можете использовать следующий код:
fs.writeFile('log.txt', 'Hello Node', { flag: "a+" }, (err) => {
if (err) throw err;
console.log('Файл создан, если он не существовал!!');
});
Обратите внимание, что флаг a+
позволяет вам открывать файл для чтения и записи. Если файл не существует, он будет создан. В случае возникновения ошибки, она будет выброшена, и вы получите сообщение в консоли.
Документация: Node.js fs.flags
Кроме appendFile
, вы также можете передать флаг в writeFile
, чтобы добавить данные в существующий файл.
fs.writeFile('log.txt', 'Hello Node', {'flag': 'a'}, function(err) {
if (err) {
return console.error(err);
}
});
Передав флаг 'a', данные будут добавлены в конец файла.
Ваш подход действительно интересен. Использование метода WriteStream
без фактического закрытия дескриптора файла через stream.end()
имеет свои преимущества, и ваша реализация с помощью cork
/uncork
может быть очень эффективной в определённых сценариях.
По вашему примеру, код выглядит довольно просто и понятно, но заметим, что вместо бесконечного цикла for(true)
в production-коде действительно будет лучше обрабатывать сообщения из websocket. Также стоит отметить, что использование cork
и uncork
позволяет минимизировать нагрузку на оперативную память и более безопасно управлять процессом записи, что особенно важно при ведении логов.
Ваш код с записью выглядит следующим образом:
var stream = fs.createWriteStream("log.txt", {flags:'a'});
for(true) {
stream.cork();
stream.write("some content to log");
process.nextTick(() => stream.uncork());
}
uncork
действительно выталкивает данные в файл в следующем такте, что может повысить общую производительность системы, особенно в условиях высокой нагрузки, как вы и упомянули, до 200 записей в секунду. Уменьшая использование оперативной памяти и повышая стабильность вашей системы в пиковые моменты, ваша реализация может стать отличным решением для приложения, где важно ведение логов, не теряя при этом производительности.
Так что ваша стратегия вполне оправдана и может быть рекомендована, особенно для тех случаев, когда требуется обработка высоких пиков записи и при меньшей активности.
Запись в файлы в Node.js
Использование async/await с циклом forEach
Как получить полный объект в console.log() Node.js, а не '[Object]'?
Как прочитать JSON-файл в память сервера?
Обнаружена ошибка: Невозможное нарушение: Неверный тип элемента: ожидался строковый тип (для встроенных компонентов) или класс/функция, но получен объект