简体   繁体   English

如何附加到节点中的文件?

[英]How to append to a file in Node?

I am trying to append a string to a log file.我正在尝试字符串附加到日志文件中。 However writeFile will erase the content each time before writing the string.但是 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'

Any idea how to do this the easy way?知道如何以简单的方式做到这一点吗?

For occasional appends, you can use appendFile , which creates a new file handle each time it's called:对于偶尔的追加,您可以使用appendFile ,它会在每次调用时创建一个新的文件句柄:

Asynchronously : 异步

const fs = require('fs');

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

Synchronously : 同步

const fs = require('fs');

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

But if you append repeatedly to the same file, it's much better to reuse the file handle .但是如果你重复追加到同一个文件, 重用文件句柄会好得多。

When you want to write in a log file, ie appending data to the end of a file, never use appendFile .当您想写入日志文件时,即将数据附加到文件末尾,切勿使用appendFile appendFile opens a file handle for each piece of data you add to your file, after a while you get a beautiful EMFILE error. appendFile为您添加到文件中的每条数据打开一个文件句柄,一段时间后您会收到一个漂亮的EMFILE错误。

I can add that appendFile is not easier to use than a WriteStream .我可以补充一点, appendFile并不比WriteStream更容易使用。

Example with appendFile : 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());

Up to 8000 on my computer, you can append data to the file, then you obtain this:在我的电脑上最多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' }

Moreover, appendFile will write when it is enabled, so your logs will not be written by timestamp.此外, appendFile会在启用时写入,因此您的日志不会按时间戳写入。 You can test with example, set 1000 in place of 100000, order will be random, depends on access to file.您可以通过示例进行测试,设置 1000 代替 100000,顺序将是随机的,取决于对文件的访问。

If you want to append to a file, you must use a writable stream like this:如果要附加到文件,则必须使用像这样的可写流:

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

You end it when you want.你在你想要的时候结束它。 You are not even required to use stream.end() , default option is AutoClose:true , so your file will end when your process ends and you avoid opening too many files.您甚至不需要使用stream.end() ,默认选项是AutoClose:true ,因此您的文件将在您的进程结束时结束并且您避免打开太多文件。

Your code using createWriteStream creates a file descriptor for every write.您使用 createWriteStream 的代码为每次写入创建一个文件描述符。 log.end is better because it asks node to close immediately after the write. 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');

Besides appendFile , you can also pass a flag in writeFile to append data to an existing file.除了appendFile ,您还可以在writeFile传递一个标志以将数据附加到现有文件。

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

By passing flag 'a', data will be appended at the end of the file.通过传递标志'a',数据将被附加到文件的末尾。

You need to open it, then write to it.您需要打开它,然后写入。

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

Here's a few links that will help explain the parameters这是一些有助于解释参数的链接

open打开
write
close关闭


EDIT : This answer is no longer valid, look into the new fs.appendFile method for appending.编辑:此答案不再有效,请查看用于追加的新fs.appendFile方法。

Use a+ flag to append and create a file (if doesn't exist):使用a+标志附加创建一个文件(如果不存在):

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

Docs: https://nodejs.org/api/fs.html#fs_file_system_flags文档: https : //nodejs.org/api/fs.html#fs_file_system_flags

Node.js 0.8 has fs.appendFile : 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!');
});

Documentation 文档

My approach is rather special.我的方法比较特殊。 I basically use the WriteStream solution but without actually 'closing' the fd by using stream.end() .我基本上使用WriteStream解决方案,但实际上没有通过使用stream.end()来“关闭” fd。 Instead I use cork / uncork .相反,我使用cork / uncork This got the benefit of low RAM usage (if that matters to anyone) and I believe it's more safe to use for logging/recording (my original use case).这获得了低 RAM 使用率的好处(如果这对任何人都很重要),我相信用于日志记录/记录(我的原始用例)更安全。

Following is a pretty simple example.下面是一个非常简单的例子。 Notice I just added a pseudo for loop for showcase -- in production code I am waiting for websocket messages.请注意,我刚刚为展示添加了一个伪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 will flush the data to the file in the next tick. uncork将在下一个滴答中将数据刷新到文件中。

In my scenario there are peaks of up to ~200 writes per second in various sizes.在我的场景中,各种大小的峰值高达每秒约 200 次写入。 During night time however only a handful writes per minute are needed.然而,在夜间,每分钟只需要少量写入。 The code is working super reliable even during peak times.即使在高峰时段,该代码也能非常可靠地工作。

Using fs.appendFile or fsPromises.appendFile are the fastest and the most robust options when you need to append something to a file.当您需要将某些内容附加到文件时,使用fs.appendFilefsPromises.appendFile是最快和最可靠的选项。

In contrast to some of the answers suggested, if the file path is supplied to the appendFile function, It actually closes by itself .与建议的某些答案相反,如果将文件路径提供给appendFile函数,它实际上会自行关闭 Only when you pass in a filehandle that you get by something like fs.open() you have to take care of closing it.只有当您传入通过fs.open()类的文件句柄时,您才需要注意关闭它。

I tried it with over 50,000 lines in a file.我在一个文件中尝试了超过 50,000 行。

Examples :例子 :

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

在此处输入图片说明

Ref: https://github.com/nodejs/node/issues/7560参考: https : //github.com/nodejs/node/issues/7560

If you want an easy and stress-free way to write logs line by line in a file, then I recommend fs-extra :如果您想要一种简单无压力的方式在文件中逐行写入日志,那么我推荐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');

Tested with Node v8.9.4.使用 Node v8.9.4 测试。

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

使用jfile包:

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

I offer this suggestion only because control over open flags is sometimes useful, for example, you may want to truncate it an existing file first and then append a series of writes to it - in which case use the 'w' flag when opening the file and don't close it until all the writes are done.我提供这个建议只是因为对打开标志的控制有时很有用,例如,您可能希望首先将其截断为现有文件,然后向其附加一系列写入 - 在这种情况下,在打开文件时使用“w”标志并且在所有写入完成之前不要关闭它。 Of course appendFile may be what you're after :-)当然 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!');
      });
    });
  });

Try to use flags: 'a' to append data to a file尝试使用flags: 'a'将数据附加到文件

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

Here's a full script.这是一个完整的脚本。 Fill in your file names and run it and it should work!填写您的文件名并运行它,它应该可以工作! Here's a video tutorial on the logic behind the script.这是有关脚本背后逻辑的视频教程

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

an easier way to do this is 一个更简单的方法是

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');
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');
}

In addition to denysonique's answer , sometimes asynchronous type of appendFile and other async methods in NodeJS are used where promise returns instead of callback passing.除了denysonique 的 answer 之外,有时在appendFile中使用异步类型的appendFile和其他异步方法,其中 promise 返回而不是回调传递。 To do it you need to wrap the function with promisify HOF or import async functions from promises namespace:为此,您需要使用promisify HOF 包装函数或从 promises 命名空间导入异步函数:

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

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

I hope it'll help 😉我希望它会有所帮助😉

I wrapped the async fs.appendFile into a Promise-based function.我将 async fs.appendFile 包装成一个基于 Promise 的函数。 Hope it helps others to see how this would work.希望它可以帮助其他人了解这将如何工作。

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

            }

        });

    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM