Як додати файл до вузла?


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

59
Хтось знає, чи fs.appendFile зберігає посилання на файл відкритим, щоб додатки були швидшими? (а не відкривати / закривати кожен запис) nodejs.org/api/…
nelsonic

7
У випадку, якщо це зручно: Зверніть увагу, що це асинхроніка. Це може спричинити за собою дивні терміни та інші речі. Наприклад: якщо у вас є process.exit()лише після цього fs.appendFile, ви можете вийти, перш ніж вихід буде надісланий. (Використання returnпрекрасно.)
SilentSteel

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

5
@chrisdew Дякую за оновлення .. але ... якщо ми не хочемо використовувати тут прийняту відповідь, що нам робити? Як ти вирішив цю дилему?
zipzit

214

Коли ви хочете записати файл журналу, тобто додавання даних до кінця файлу, ніколи не використовуйте appendFile. appendFileвідкриває обробку файлів для кожного фрагмента даних, який ви додаєте у свій файл, через деякий час ви отримуєте гарну EMFILEпомилку.

Можу додати, що appendFileце не простіше у використанні, ніж a 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 є синхронним. Просто трапляється надати певні способи управління асинхронними операціями, наприклад, Обіцяння та асинхронізація / очікування.
Шарку

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

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 = requ (' fs ');'
Stormbytes

5
Або, можливо, навіть краще var fs = require('graceful-fs'), що згадало деякі відомі проблеми. Докладнішу інформацію див. У документах .
Марко Боначі

3
І початковий, і кінцевий рядок знаходяться на одній лінії, хоча :-p
бінкі

5
Зверніть увагу : якщо ви користуєтесь fs.createWriteStream, тоді використовуйте flags. Якщо ви використовуєте, fs.writeFileто це flag. Для отримання додаткової інформації зверніться до Документи Jode Docs - File System .
Аніш Найр

2
Будь обережний! Параметр - не "прапори", а "прапор" (сингулярно): nodejs.org/api/…
Benny Neugebauer

25

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

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

Передаючи прапор "a", дані будуть додані в кінці файлу.


3
Зверніть увагу : якщо ви користуєтесь fs.createWriteStream, тоді використовуйте flags. Якщо ви використовуєте, fs.writeFileто це flag. Для отримання додаткової інформації зверніться до Документи Jode 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
виглядають як суперкобра постійно записує журнал у файл журналу, використання 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
що-небудь синхронізація () майже завжди є поганою ідеєю, якщо ви не впевнені на 100%, що абсолютно НЕОБХІДНО. Вже тоді ти, мабуть, робиш це неправильно.
Зейн Клайз

4
Не означає, що це неправильно. Це просто робить це синхронно. Можливо, це не найкраща практика для Node.js, але вона підтримується.
Луїс Р.

2
Я використовував "ур зроби це неправильно" у розмовному сенсі цієї фрази через Інтернет-мему. Очевидно, що це підтримується = P
Zane Claes

7
Домовилися про асинхронізацію, але іноді, якщо ви просто пишете інтерактивний сценарій, синхронізація прекрасна.
bryanmac

7
Писати синхронно абсолютно нормально, якщо ви робите додаток для командного рядка з одним користувачем (наприклад, сценарій, щоб виконати деякі речі). Так швидше робити речі. Чому б у вузла були методи синхронізації, якщо не для цього?
Ян Święcki

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цикл для вітрини - у виробничому коді я чекаю повідомлень веб-сокетів.

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()ви повинні подбати про його закриття.

Я спробував це з понад 50 000 рядків у файлі.

Приклади:

(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/isissue/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.