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


505

Я пытаюсь добавить строку в файл журнала. Однако 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'

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

Ответы:


799

Вы можете использовать случайные добавления, 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');

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


6
Начиная с Node v0.8.0
denysonique

59
Кто-нибудь знает, если fs.appendFile держит ссылку на файл открытым, чтобы добавления происходили быстрее? (вместо того, чтобы открывать / закрывать каждую запись) nodejs.org/api/…
nelsonic

7
В случае, если это удобно: обратите внимание, что это асинхронно. Это может привести к странным срокам и другим вещам. Пример: если у вас есть process.exit()только после fs.appendFile, вы можете выйти до отправки вывода. (Использование returnв порядке.)
SilentSteel

6
В худшем случае вы можете использовать синхронную версию appendFileSync. nodejs.org/api/… Но вы можете потерять одно из больших преимуществ Node - асинхронные операции. Убедитесь, что вы ловите ошибки. Возможно, в некоторых ОС вы можете получить доступ, если запрашиваете дескриптор файла одновременно. Не уверен насчет этого.
SilentSteel

5
@ chrisdew Спасибо за обновление .. но ... если мы не хотим использовать здесь принятый ответ, что мы должны делать? Как вы решили эту дилемму?
zipzit

215

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


2
Это место.
Погриндис

3
Спасибо за отличный ответ, но я сомневаюсь, что из-за асинхронной природы Javascript он будет выполняться stream.end()до stream.write(), поэтому мы не должны использовать stream.end(), также как вы упомянули, что AutoClose:Trueэто опция по умолчанию, тогда зачем писать строку, которая не имеет смысла? использовать.
Аашиш Кумар

13
due to asynchronous nature of Javascript... Какая? Array.forEach - синхронная операция. JS является синхронным. Это просто обеспечивает некоторые способы управления асинхронными операциями, такие как Promises и async / await.
Sharcoux

1
Я предполагаю, что fs.appendFileрезультатом будет слишком много открытых файлов, потому что вы выполняете его асинхронно (вы просто асинхронно создаете дескриптор файла 10000), я полагаю, appendFileSyncчто не возникнет подобной проблемы, также не fs.appendFileс правильным интервалом (1 с, вероятно, более чем достаточно) или в очереди.
яблоко яблоко

1
@RedwolfPrograms Для журнала занятого сервера, может быть и правда. Может быть, нет. В любом случае, я просто заявляю, что точка (по крайней мере, причина) в этом ответе не верна.
яблоко яблоко

122

Ваш код с использованием 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');

6
отсутствует первая строка! должно быть 'var fs = require (' fs ');'
Stormbytes

5
Или, может быть, даже лучше var fs = require('graceful-fs'), что выветрило некоторые известные проблемы. Смотрите документы для получения дополнительной информации.
Марко Боначи

3
И начальная, и конечная строки находятся на одной линии :-p
binki

5
Обратите внимание : если вы используете fs.createWriteStream, то используйте flags. Если вы используете, fs.writeFileто это flag. Пожалуйста, обратитесь к Node JS Docs - File System для получения дополнительной информации.
Аниш Наир

2
Быть осторожен! Параметр не "flags", а "flag" (в единственном числе): nodejs.org/api/…
Benny Neugebauer

25

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

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

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


3
Обратите внимание : если вы используете fs.createWriteStream, то используйте flags. Если вы используете, fs.writeFileто это flag. Пожалуйста, обратитесь к Node JS Docs - File System для получения дополнительной информации.
Аниш Наир

19

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

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 для добавления.


1
похоже, что supercobra постоянно записывает журнал в файл журнала, в этом случае использование fs.write не рекомендуется, вместо этого используйте fs.createWriteStream. Прочитайте nodejs.org/docs/v0.4.8/api/all.html#fs.write
злой киви

10
Ответ больше не является точным, как для nodejs v0.4.10.
Рубен Тан

это должно быть «0666», а не 666.
Я Чжуан


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

5
что-нибудь sync () - почти всегда плохая идея, если вы не уверены на 100%, что вам НУЖНО. Даже тогда вы, вероятно, делаете это неправильно.
Зейн Клэйс

4
Это не значит, что это неправильно. Это просто делает это синхронно. Не может быть лучшей практикой для Node.js, но это поддерживается.
Луис Р.

2
Я использовал «ты делаешь это неправильно» в разговорной фразе «интернет-мем». Очевидно, это поддерживается = P
Zane Claes

7
Договорились об асинхронности, но иногда, если вы просто пишете интерактивный скрипт, синхронизация в порядке.
bryanmac

7
Синхронное написание абсолютно нормально, если вы работаете с однопользовательским приложением командной строки (например, сценарием, чтобы сделать что-то). Таким образом, это быстрее делать вещи. Зачем узлу иметь методы синхронизации, если не для этой цели?
Ян Свенцкий

3

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


2

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

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

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

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

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


1

Я предлагаю это предложение только потому, что контроль над флагами открытия иногда полезен, например, вы можете сначала урезать существующий файл, а затем добавить серию записей в него - в этом случае при открытии файла используйте флаг '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!');
      });
    });
  });


1

Использование 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


0

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

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);

0

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

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

Проще чем что? Вы проверили верхний ответ, который дает точно такое же appendFileSyncрешение?
Дан Дакалеску

0
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');
}

0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.