【问题标题】:How to append to a file in Node?如何追加到节点中的文件?
【发布时间】:2022-01-14 05:25:15
【问题描述】:

我正在尝试追加一个字符串到日志文件。但是 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'

知道如何简单地做到这一点吗?

【问题讨论】:

    标签: javascript node.js fs appendfile


    【解决方案1】:

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

    但是如果你重复追加到同一个文件,最好reuse the file handle

    【讨论】:

    • 有谁知道 fs.appendFile 是否保持打开文件的链接以便追加速度更快? (而不是打开/关闭每次写入)nodejs.org/api/…
    • @nelsonic 根据源码,没有特别处理。
    • 如果方便的话:注意这是异步的。这可能会导致奇怪的时机和其他事情。例如:如果你在fs.appendFile 之后有process.exit(),你可以在输出发送之前退出。 (使用return 很好。)
    • 更糟糕的情况,你可以使用同步版本,appendFileSyncnodejs.org/api/… 但是你可能会失去 Node 的一大好处,那就是异步操作。确保捕获错误。也许在某些操作系统上,如果同时请求文件句柄,您可能会被拒绝访问。不确定。
    • @chrisdew 感谢您的更新......但是......如果我们不在这里使用接受的答案,我们应该怎么做?你是如何解决这个困境的?
    【解决方案2】:

    当您想写入日志文件时,即将数据附加到文件末尾,永远不要使用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,因此您的文件将在您的进程结束时结束,并且您避免打开太多文件。

    【讨论】:

    • 感谢您的出色回答,但我怀疑由于 Javascript 的异步性质,它会在 stream.write() 之前执行 stream.end(),所以我们不应该使用 stream.end(),也作为你提到AutoClose:True 是一个默认选项,那么为什么还要写一个没有用的行。
    • due to asynchronous nature of Javascript... 什么? Array.forEach 是一个同步操作。 JS 是同步的。它恰好提供了一些管理异步操作的方法,例如 Promises 和 async/await。
    • 我猜fs.appendFile 会导致打开的文件太多,因为您以异步方式执行它(您只是异步创建10000 个文件句柄),我相信appendFileSync 不会有类似的问题,也不是 fs.appendFile 有适当的间隔(1s 可能绰绰有余)或排队。
    • @appleapple 但是您每次仍然打开文件。对于日志,保持打开状态更有意义。
    • @RedwolfPrograms 对于繁忙的服务器日志,也许是真的。对于每个执行日志一次,也许不是。无论如何,我只是声明这个答案中的观点(至少是原因)是不正确的。
    【解决方案3】:

    您的代码使用 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');
    

    【讨论】:

    • 缺少第一行!应该是 'var fs = require('fs');'
    • 或者更好的var fs = require('graceful-fs'),它解决了一些已知问题。请参阅docs 了解更多信息。
    • 虽然起始行和结束行都在同一行:-p
    • 请注意:如果您使用的是fs.createWriteStream,请使用flags。如果您使用的是fs.writeFile,那么它就是flag。更多信息请参考Node JS Docs - File System
    • @BennyNeugebauer 使用标志是正确的,你不是。在您将文档链接到确实使用“标志”的 fs.writeFile 之前,它已经发布了好几个月。但是这个解决方案使用 fs.createWriteStream 并且参数 'flags' 是正确的 - nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
    【解决方案4】:

    除了appendFile,您还可以在writeFile 中传递一个标志来将数据附加到现有文件。

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

    通过传递标志'a',数据将附加到文件的末尾。

    【讨论】:

    • 请注意:如果您使用的是fs.createWriteStream,请使用flags。如果您使用的是fs.writeFile,那么它就是flag。更多信息请参考Node JS Docs - File System
    【解决方案5】:

    你需要打开它,然后写入它。

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

    这里有几个链接可以帮助解释参数

    open
    write
    close


    编辑:此答案不再有效,请查看新的fs.appendFile 追加方法。

    【讨论】:

    • 看起来 supercobra 不断的将日志写入日志文件,这种情况下不推荐使用 fs.write,改用 fs.createWriteStream。阅读nodejs.org/docs/v0.4.8/api/all.html#fs.write
    • 从 nodejs v0.4.10 开始,答案不再准确。
    • 应该是 '0666' 而不是 666。
    【解决方案6】:

    使用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

    【讨论】:

      【解决方案7】:

      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

      【讨论】:

        【解决方案8】:

        我的方法比较特别。我基本上使用WriteStream 解决方案,但实际上没有使用stream.end() '关闭' fd。相反,我使用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 次。然而,在夜间,每分钟只需要少量写入。即使在高峰时段,该代码的运行也非常可靠。

        【讨论】:

          【解决方案9】:

          当您需要将某些内容附加到文件时,使用 fs.appendFilefsPromises.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

          【讨论】:

            【解决方案10】:

            如果您想要一种简单且无压力的方式在文件中逐行写入日志,那么我推荐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 测试。

            【讨论】:

              【解决方案11】:
              fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
              fs.writeSync(fd, 'contents to append')
              fs.closeSync(fd)
              

              【讨论】:

              • 任何东西 sync() 几乎总是一个坏主意,除非你 100% 确定你绝对需要它。即使那样,你也可能做错了。
              • 不代表错了。它只是同步进行。可能不是 Node.js 的最佳实践,但它受到支持。
              • 我在口语的互联网模因意义上使用“你做错了”。显然是支持的=P
              • 同意异步,但有时如果你只是写一个交互式脚本,同步就可以了。
              • 如果您正在执行单用户命令行应用程序(例如执行某些操作的脚本),同步写入绝对没问题。这样,做事会更快。如果不是为了这个目的,节点为什么会有同步方法?
              【解决方案12】:

              使用jfile 包:

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

              【讨论】:

                【解决方案13】:

                我提出这个建议只是因为对打开标志的控制有时很有用,例如,您可能希望先将其截断为现有文件,然后 然后 对其附加一系列写入 - 在这种情况下打开文件时使用'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!');
                      });
                    });
                  });
                

                【讨论】:

                  【解决方案14】:

                  尝试使用flags: 'a' 将数据附加到文件中

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

                  【讨论】:

                    【解决方案15】:

                    这是一个完整的脚本。填写您的文件名并运行它,它应该可以工作! 这是关于脚本背后逻辑的video tutorial

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

                    【讨论】:

                      【解决方案16】:
                      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');
                      }
                      

                      【讨论】:

                        【解决方案17】:

                        除了denysonique's answer,有时在NodeJS 中使用异步类型appendFile 和其他异步方法用于promise 返回而不是回调传递。为此,您需要使用 promisify HOF 包装函数或从 promises 命名空间导入异步函数:

                        const { appendFile } = require('fs').promises;
                        
                        await appendFile('path/to/file/to/append', dataToAppend, optionalOptions);
                        

                        我希望它会有所帮助?

                        【讨论】:

                          【解决方案18】:

                          我将 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);
                          
                                      }
                          
                                  });
                          
                              }
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2019-03-16
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多