【问题标题】:How to write incrementally to a text file and flush output如何增量写入文本文件并刷新输出
【发布时间】:2017-09-24 09:41:30
【问题描述】:

我的 Node.js 程序 - 这是一个普通的命令行程序,基本上不会做任何操作上不寻常的事情,没有任何系统特定或异步或类似的事情 - 需要不时将消息写入文件,然后它会被 ^C 打断,它需要文件的内容仍然存在。

我尝试过使用fs.createWriteStream,但结果只是一个 0 字节的文件。 (如果程序通过运行主文件的末尾而结束,则该文件确实包含文本,但这不是我所拥有的场景。)

我尝试过使用winston,但最终根本没有创建文件。 (如果程序通过运行主文件的末尾而结束,则该文件确实包含文本,但这不是我所拥有的场景。)

fs.writeFile 可以在您预先准备好所有要编写的文本时完美运行,但似乎不支持一次追加一行。

推荐的方法是什么?

编辑:我尝试过的特定代码:

var fs = require('fs')

var log = fs.createWriteStream('test.log')
for (var i = 0; i < 1000000; i++) {
    console.log(i)
    log.write(i + '\n')
}

运行几秒钟,点击 ^C,留下一个 0 字节的文件。

【问题讨论】:

  • 你能分享你尝试过的每个部分的代码吗,这个问题很模糊,没有真正展示你是如何使用 writeStream、winston 等的。
  • createWriteStream 应该适用于这种情况。 write 调用的返回值是多少?如果它返回false,您应该在drain 处理程序中进行进一步的写入,或者它只是继续缓冲您对内存的写入。另外,您是否在循环中编写而不释放 CPU?发布一些代码会有所帮助。
  • @Abhishek 好的,为 createWriteStream 添加了特定代码。
  • @Vasan 在不释放 CPU 的情况下在循环中写入,是的,这就是我的程序所做的,它是一个长时间运行的计算工作负载,它不是 Web 服务器或类似的东西。进一步写入排水处理程序 - 我不确定这意味着什么,没有排水处理程序,如果有,程序的其余部分不会位于其中?

标签: node.js file


【解决方案1】:

原来 Node 提供了一个似乎工作正常的较低级别的文件 I/O API!

var fs = require('fs')

var log = fs.openSync('test.log', 'w')
for (var i = 0; i < 100000; i++) {
    console.log(i)
    fs.writeSync(log, i + '\n')
}

【讨论】:

  • @rwallace 所以这可以在没有任何缓冲的情况下工作并立即写入文件?
【解决方案2】:

NodeJS 不能以传统方式工作。它使用单个线程,因此通过运行一个大循环并在内部执行 I/O,您没有机会(即释放 CPU)执行其他异步操作,例如:将内存缓冲区刷新到实际文件。

逻辑必须是 - 执行一个 write,然后将您的函数(调用写入)作为回调传递给 process.nextTick 或作为回调写入流的 drain 事件(如果缓冲区在上次写入期间已满)。

这是一个快速而肮脏的版本,可以满足您的需求。请注意,没有长时间运行的循环或其他 CPU 阻塞,而是我安排我的后续写入以供将来使用并快速返回,暂时释放 CPU 用于其他事情。

var fs = require('fs')

var log = fs.createWriteStream('test.log');
var i = 0;

function my_write() {
if (i++ < 1000000)
{
    var res = log.write("" + i + "\r\n");
    if (!res) {
        log.on('drain',my_write);
    } else {
        process.nextTick(my_write);
    }
    console.log("Done" + i + " " + res + "\r\n");
}
}

my_write();

【讨论】:

  • 好的,所以你说我需要放弃 Node.js 并使用不同的语言。虽然我不能确定情况并非如此,但对于其他似乎运作良好的事情似乎过于悲观。另一边的草不是绿的;其他语言的问题比这看起来更糟糕。
  • 我不是这么说的。我已经将 node 用于您现在使用它的确切目的。它只是需要一种稍微不同的思维方式。让我添加一些代码来更好地解释。
  • @rwallace 我添加了一个代码示例 - 请看看它是否有助于您的理解。
  • 好的,我可能最终会不这样做,但无论如何感谢您的建议,赞成。
  • 在异步函数中,您应该可以使用log.write(...) || await new Promise(resolve =&gt; log.once('drain',resolve)); 来保持写操作的单行。
【解决方案3】:

这个功能也可能有用。

/**
 * Write `data` to a `stream`. if the buffer is full will block
 * until it's flushed and ready to be written again.
 * [see](https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback)
 */
export function write(data, stream) {
  return new Promise((resolve, reject) => {
    if (stream.write(data)) {
      process.nextTick(resolve);
    } else {
      stream.once("drain", () => {
        stream.off("error", reject);
        resolve();
      });
      stream.once("error", reject);
    }
  });
}

【讨论】:

    【解决方案4】:

    您正在使用 for 循环写入文件,这很糟糕,但这是另一种情况。首先 createWriteStream 不会自动关闭文件,您应该调用 close。 如果你在 for 循环之后立即调用 close,它会在不写入的情况下关闭,因为它是异步的。

    欲了解更多信息,请阅读此处:https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options

    问题是 for 循环中的异步函数。

    【讨论】:

    • 那个文档说自动关闭默认为 true?
    • 是的,但您应该调用 close。由于您的脚本终止如此之快,您看不到文件写入。在脚本结束时尝试 setTimeout 5 秒。
    猜你喜欢
    • 2015-07-28
    • 2015-06-19
    • 2016-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-22
    • 2013-01-20
    • 1970-01-01
    相关资源
    最近更新 更多