7

Как добавить данные в файл в Node?

3

Я пытаюсь добавить строку в файл журнала. Однако функция writeFile каждый раз стирает содержимое файла перед записью новой строки.

fs.writeFile('log.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('Сохранено!');
}); // => log.txt очищен, содержит только 'Hello Node'

Есть ли у кого-нибудь идеи, как сделать это проще?

5 ответ(ов)

4

Когда вы хотите записывать данные в лог-файл, т.е. добавлять информацию в конец файла, никогда не используйте 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, и ваш файл будет закрыт, когда завершится ваш процесс, что позволяет избежать открытия слишком большого количества файлов.

1

Ваш код, использующий 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() гарантирует, что поток завершится корректно и файл будет закрыт, что может помочь избежать утечек ресурсов.

0

Чтобы использовать флаги a+ для добавления и создания файла (если он не существует), вы можете использовать следующий код:

fs.writeFile('log.txt', 'Hello Node', { flag: "a+" }, (err) => {
  if (err) throw err;
  console.log('Файл создан, если он не существовал!!');
});

Обратите внимание, что флаг a+ позволяет вам открывать файл для чтения и записи. Если файл не существует, он будет создан. В случае возникновения ошибки, она будет выброшена, и вы получите сообщение в консоли.

Документация: Node.js fs.flags

0

Кроме appendFile, вы также можете передать флаг в writeFile, чтобы добавить данные в существующий файл.

fs.writeFile('log.txt', 'Hello Node', {'flag': 'a'}, function(err) {
    if (err) {
        return console.error(err);
    }
});

Передав флаг 'a', данные будут добавлены в конец файла.

0

Ваш подход действительно интересен. Использование метода 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 записей в секунду. Уменьшая использование оперативной памяти и повышая стабильность вашей системы в пиковые моменты, ваша реализация может стать отличным решением для приложения, где важно ведение логов, не теряя при этом производительности.

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь