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

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

fs.writeFile('log.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
}); // => message.txt erased, contains only 'Hello Node'

Есть идеи, как сделать это простым способом?

Даниил

12 ответов

Решение

Для случайных добавлений вы можете использовать appendFile, который создает новый дескриптор файла каждый раз, когда он вызывается:

Асинхронно:

const fs = require('fs');

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('Saved!');
});

Синхронно:

const fs = require('fs');

fs.appendFileSync('message.txt', 'data to append');

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

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

var fs = require('fs');
var logStream = fs.createWriteStream('log.txt', {'flags': 'a'});
// use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
logStream.write('Initial line...');
logStream.end('this is the end line');

Использовать a+флаг для добавления и создания файла (если не существует):

fs.writeFile('log.txt', 'Hello Node', { flag: "a+" }, (err) => {
  if (err) throw err;
  console.log('The file is created if not existing!!');
}); 

Документы: https://nodejs.org/api/fs.html#fs_file_system_flags

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

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

Если передать флаг 'a', данные будут добавлены в конец файла.

Вам нужно открыть его, а затем написать в него.

var fs = require('fs'), str = 'string to append to file';
fs.open('filepath', 'a', 666, function( e, id ) {
  fs.write( id, 'string to append to file', null, 'utf8', function(){
    fs.close(id, function(){
      console.log('file closed');
    });
  });
});

Вот несколько ссылок, которые помогут объяснить параметры

открыть
записывать
близко


РЕДАКТИРОВАТЬ: Этот ответ больше не действителен, посмотрите на новый метод fs.appendFile для добавления.

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

В отличие от некоторых из предложенных ответов, если путь к файлу указан в appendFileфункция, она фактически закрывается сама по себе. Только когда вы передаете дескриптор файла, вы получаете что-то вродеfs.open() вы должны позаботиться о его закрытии.

Я пробовал это с более чем 50000 строк в файле.

Примеры:

(async () => {
  // using appendFile.
  const fsp = require('fs').promises;
  await fsp.appendFile(
    '/path/to/file', '\r\nHello world.'
  );

  // using apickfs; handles error and edge cases better.
  const apickFileStorage = require('apickfs');
  await apickFileStorage.writeLines(
    '/path/to/directory/', 'filename', 'Hello world.'
  );
})();

Ссылка: https://github.com/nodejs/node/issues/7560
Пример выполнения: https://github.com/apickjs/apickFS/blob/master/writeLines.js

У меня особенный подход. Я в основном используюWriteStream решение, но без фактического закрытия fd с помощью stream.end(). Вместо этого я используюcork/uncork. Это дает преимущество низкого использования ОЗУ (если это кому-то важно), и я считаю, что его более безопасно использовать для ведения журнала / записи (мой первоначальный вариант использования).

Ниже приводится довольно простой пример. Обратите внимание, я только что добавил псевдоfor цикл для витрины - в производственном коде я жду сообщений веб-сокета.

var stream = fs.createWriteStream("log.txt", {flags:'a'});
for(true) {
  stream.cork();
  stream.write("some content to log");
  process.nextTick(() => stream.uncork());
}

uncork сбросит данные в файл в следующем тике.

В моем сценарии есть пики до ~200 операций записи в секунду в различных размерах. Однако в ночное время требуется лишь несколько операций записи в минуту. Код работает очень надежно даже в часы пик.

Узел 0.8 имеет fs.appendFile:

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

Документы: http://nodejs.org/docs/latest/api/fs.html

Если вам нужен простой и удобный способ записи журналов построчно в файле, я рекомендую fs-extra:

const os = require('os');
const fs = require('fs-extra');

const file = 'logfile.txt';
const options = {flag: 'a'};

async function writeToFile(text) {
  await fs.outputFile(file, `${text}${os.EOL}`, options);
}

writeToFile('First line');
writeToFile('Second line');
writeToFile('Third line');
writeToFile('Fourth line');
writeToFile('Fifth line');

Протестировано с Node v8.9.4.

fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
fs.writeSync(fd, 'contents to append')
fs.closeSync(fd)

Я предлагаю это предложение только потому, что контроль над флагами открытия иногда полезен, например, вы можете сначала урезать существующий файл, а затем добавить серию записей в него - в этом случае при открытии файла используйте флаг 'w' и не закрывайте его, пока все записи не будут завершены. Конечно, appendFile может быть тем, что вам нужно:-)

  fs.open('log.txt', 'a', function(err, log) {
    if (err) throw err;
    fs.writeFile(log, 'Hello Node', function (err) {
      if (err) throw err;
      fs.close(log, function(err) {
        if (err) throw err;
        console.log('It\'s saved!');
      });
    });
  });

Используя пакет jfile:

myFile.text+='\nThis is new line to be appended'; //myFile=new JFile(path);

Попробуй использовать flags: 'a' добавить данные в файл

 var stream = fs.createWriteStream("udp-stream.log", {'flags': 'a'});
  stream.once('open', function(fd) {
    stream.write(msg+"\r\n");
  });

Более простой способ сделать это

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');

Я превратил async fs.appendFile в функцию на основе Promise. Надеюсь, это поможет другим увидеть, как это будет работать.

    append (path, name, data) {

        return new Promise(async (resolve, reject) => {

            try {

                fs.appendFile((path + name), data, async (err) => {

                    if (!err) {

                        return resolve((path + name));

                    } else {

                        return reject(err);

                    }

                });

            } catch (err) {

                return reject(err);

            }

        });

    }

Вот полный сценарий. Заполните ваши имена файлов и запустите его, и он должен работать! Вот видеоурок по логике сценария.

var fs = require('fs');

function ReadAppend(file, appendFile){
  fs.readFile(appendFile, function (err, data) {
    if (err) throw err;
    console.log('File was read');

    fs.appendFile(file, data, function (err) {
      if (err) throw err;
      console.log('The "data to append" was appended to file!');

    });
  });
}
// edit this with your file names
file = 'name_of_main_file.csv';
appendFile = 'name_of_second_file_to_combine.csv';
ReadAppend(file, appendFile);
const inovioLogger = (logger = "") => {
    const log_file = fs.createWriteStream(__dirname + `/../../inoviopay-${new Date().toISOString().slice(0, 10)}.log`, { flags: 'a' });
    const log_stdout = process.stdout;
    log_file.write(logger + '\n');
}

Из документации по nodejs.

  // Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
    fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
      if (err) throw err;
    });

В Windows использование fs.mkdir() в корневом каталоге даже с рекурсией приведет к ошибке:

    fs.mkdir('/', { recursive: true }, (err) => {
      // => [Error: EPERM: operation not permitted, mkdir 'C:\']
    });

Посмотрите на https://nodejs.org/api/fs.html

В дополнение к ответу denysonique, иногда асинхронный типappendFileи другие асинхронные методы в NodeJS используются там, где обещание возвращается вместо передачи обратного вызова. Для этого вам нужно обернуть функцию с помощьюpromisify HOF или импортируйте асинхронные функции из пространства имен обещаний:

const { appendFile } = require('fs').promises;

await appendFile('path/to/file/to/append', dataToAppend, optionalOptions);

Я надеюсь это поможет

Другие вопросы по тегам