如何在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'

任何想法如何以简单的方式做到这一点?

Answers:


798

对于偶尔的追加,您可以使用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');

但是,如果您重复追加到同一文件,最好重用文件handle


6
从Node v0.8.0开始
denysonique 2012年

59
有谁知道fs.appendFile是否保持文件打开链接,以便追加操作更快?(而不是打开/关闭每个写)nodejs.org/api/...
nelsonic

7
如果方便,请注意:这是异步的。这会导致奇怪的时间安排和其他问题。例如:如果process.exit()刚好在fs.appendFile,则可以在发送输出之前退出。(可以使用return。)
SilentSteel 2014年

6
更糟糕的是,您可以使用同步版本appendFileSyncnodejs.org/api/…但是您可能会失去Node的一大好处,那就是异步操作。确保您发现错误。也许在某些操作系统上,如果同时请求文件句柄,可能会导致访问被拒绝。不确定。
SilentSteel 2014年

5
@chrisdew感谢您的更新。但是...如果我们在这里不使用接受的答案,我们应该怎么做?您是如何解决这个难题的?
zipzit

214

当您要写入日志文件时,即将数据追加到文件末尾时,请勿使用appendFileappendFile一段时间后,您会收到一个漂亮的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
这是现场。
Pogrindis

3
感谢您的出色回答,但我的疑问是,由于Javascript的异步特性,它将在stream.end()之前执行stream.write(),因此我们不应该使用stream.end(),就像您提到的那样,这AutoClose:True是默认选项,然后为什么还要写一行没有采用。
Aashish Kumar,

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 2015年

5
甚至更好var fs = require('graceful-fs'),它消除了一些已知问题。有关更多信息,请参阅文档
Marko Bonaci 2015年

3
起始行和结束行都在同一行:-p
binki

5
请注意:如果使用fs.createWriteStream,请使用flags。如果您正在使用fs.writeFile,则为flag。有关更多信息,请参考Node JS Docs-文件系统
艾尼斯·奈尔

2
小心!该参数不是“标志”,但“旗帜”(单数):nodejs.org/api/...
班尼纽格鲍尔

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。有关更多信息,请参考Node JS Docs-文件系统
艾尼斯·奈尔

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。–
Ya Zhuang

13

Node.js 0.8具有fs.appendFile

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

文献资料


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

5
任何sync()几乎总是一个坏主意,除非您100%确保绝对需要它。即使那样,您可能还是做错了。
Zane Claes 2012年

4
并不意味着这是错误的。它只是同步执行。可能不是Node.js的最佳做法,但它受支持。
Luis R.

2
在口语化的网际模因意义上,我使用的是“ ur doin it错”。显然,它受支持= P
Zane Claes 2012年

7
同意异步,但是有时候如果您只是在编写交互式脚本,同步就可以了。
bryanmac

7
如果您正在执行单用户命令行应用程序(例如,执行某些操作的脚本),那么同步编写绝对可以。这样,做事更快。为什么节点不具有同步方法呢?
2014年

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。这具有低RAM使用率(如果这对任何人都重要)的好处,并且我认为使用它进行日志记录/记录(我的原始用例)更为安全。

以下是一个非常简单的示例。注意,我只是添加了一个伪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()需要注意关闭它。

我在一个文件中尝试了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/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解决方案的最佳答案?
Dan Dascalescu

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的答案外,有时在NodeJS中使用异步类型的appendFile和其他异步方法,在这些方法中,promise返回而不是回调传递。为此,您需要使用promisifyHOF 包装该函数或从promises命名空间导入异步函数:

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

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

希望对你有帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.