写入文件 fs 流 NodeJS 时出错

Error writing to file fs stream NodeJS

Objective

使用 csv-write-stream 和 fs 流将一个非常大的数组写入文件。

背景

我有一个小应用程序,可以将大量数据(数千个条目)写入 CSV 文件。为实现这一点,我使用了上述库,它只不过是 fs 流的掩码(方便)。但是,应用程序在 运行 时间内崩溃了,我不知道为什么。

错误

文件被创建并且流开始写入它,但是在执行过程中我总是有同样的错误:

events.js:141
      throw er; // Unhandled 'error' event
      ^

Error: write after end
    at writeAfterEnd (_stream_writable.js:166:12)
    at WriteStream.Writable.write (_stream_writable.js:211:5)
    at ondata (_stream_readable.js:536:20)
    at emitOne (events.js:77:13)
    at emit (events.js:169:7)
    at Readable.read (_stream_readable.js:368:10)
    at flow (_stream_readable.js:751:26)
    at WriteStream.<anonymous> (_stream_readable.js:609:7)
    at emitNone (events.js:67:13)
    at WriteStream.emit (events.js:166:7)

代码

我知道这个错误与这段代码有关:

let writer = csvWriter({
    headers: ['country', 'postalCode']
});
let fsStream = fs.createWriteStream('output.csv');
writer.pipe(fsStream);

//very big array !
currCountryCodes = [{country: 'A', postalCode: 0}, {country: 'B', postalCode: 1 }];

for (let j = 0; j < currCountryCodes.length; j++) {
    writer.write(currCountryCodes[j]);
}

writer.end(function() {
  fsStream.end(function() {
    console.log('=== CSV written successfully, stopping application ===');
    process.exit();
  });
});

最具体的执行:

fsStream.end(function() {
        console.log('=== CSV written successfully, stopping application ===');
        process.exit();
      });

但我不明白为什么会这样。不知何故,只有当我写的数组有数千行时才会发生这种情况,如果我只有十几行,它 运行 完全没问题。

问题

通过阅读错误,我得到的印象是我在关闭流后仍在写入文件,但这不可能发生,因为这些功能仅在 运行 所有内容都刷新到文件时(对吗?)

问题是您(可以理解)假设当调用 writer.end() 的回调时,不会再向 fsStream 写入任何数据,但事实并非如此(如错误所示).

writer 是一个 duplex stream, which means it's both readable and writable. By calling the .end() 方法,你告诉它你不会再写信给它了。但是,这并不意味着它仍然不可读。

当您随后结束 fsStream 时,某些缓冲区中仍有未刷新的数据将通过管道传输到 fsStream,从而产生错误。

您可以通过侦听 writer 上的 end 事件来解决此问题,这相当于表示没有数据可读:

writer.end(function() {
  writer.on('end', function() {
    fsStream.end(function() {
      console.log('=== CSV written successfully, stopping application ===');
      process.exit();
    });
  });
});

编辑:我认为当调用writer.end()时,fsStream也会自动结束(所以fsStream.end()是多余的,它的回调可能不会被调用,因为 finish 事件已经发出。

如果您想确保所有数据都已刷新到输出文件,您可以在 fsStream 上监听 finish 事件:

writer.end(function() {
  fsStream.on('finish', function() {
    console.log('=== CSV written successfully, stopping application ===');
    process.exit();
  });
});

甚至:

fsStream.on('finish', function() {
  console.log('=== CSV written successfully, stopping application ===');
  process.exit();
});

writer.end();