Как добавить файл в 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);
Я надеюсь это поможет