Write big file by node stream

1,040 阅读2分钟

用node stream 模块写大文件

最近在看node 的 Stream 模块 看到了一些例子

const fs = require('fs');
const file = fs.createWriteStream('./big.file');
for(let  i = 0;i<=1e6;i++) {
    file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit. \n');
}
file.end();

看看这个例子里的stream是怎么实现的

// 从fs中的stream中引入了WriteStream模块
const { WriteStream } = require('internal/fs/streams');
function createWriteStream(path, options) {
  // 返回了一个写流的示例
  return new WriteStream(path, options);
}

我们在往下面看下这个示例里面做了什么事情

const { Readable, Writable } = require('stream');
// 这里继承了 Writable 
util.inherits(WriteStream, Writable);

function WriteStream(path, options) {
   // 设置了一些参数
}

由此我们可以看出来 stream中有 Readable类和Writable类,当然还有其他两个类

  • Writable: streams to which data can be written (for example, fs.createWriteStream()).

  • Readable: streams from which data can be read (for example, fs.createReadStream()).

  • Duplex: streams that are both Readable and Writable (for example, net.Socket).

  • Transform: Duplex streams that can modify or transform the data as it is written and read (for example, zlib.createDeflate()).

这篇文章的重点是在Writable这个类,深入了解这个类后, 再去看其他的方法, 想必会有触类旁的感觉。

**

再回来最上面的demo 让我们把循环的次数调试大一些 看看会发生什么

内存溢出了... 文档中

当流还未被排空时,也是可以调用 write(),Node.js 会缓冲所有被写入的数据块,直到达到最大内存占用,这时它会无条件中止。 甚至在它中止之前, 高内存占用将会导致垃圾回收器的性能变差和 RSS 变高(即使内存不再需要,通常也不会被释放回系统)。 解释了这个现象。

那我偏要勉强一次性写这么多数据呢

writable.write() 写入数据到流,并在数据被完全处理之后调用 callback。 如果发生错误,则 callback 可能被调用也可能不被调用。 为了可靠地检测错误,可以为 'error' 事件添加监听器。

在接收了 chunk 后,如果内部的缓冲小于创建流时配置的highWaterMark,则返回 true 。 如果返回 false ,则应该停止向流写入数据,直到 'drain' 事件被触发。

当流还未被排空时,调用 write() 会缓冲 chunk,并返回 false。 一旦所有当前缓冲的数据块都被排空了(被操作系统接收并传输),则触发 'drain' 事件。 建议一旦 write() 返回 false,则不再写入任何数据块,直到 'drain' 事件被触发

这里我们看到highWaterMark属性和drain事件。

  • highWaterMark 当调用 stream.write() 开始返回 false 时的缓冲大小。 默认为 16384 (16kb), 对象模式的流默认为 16。
  • 如果调用 stream.write(chunk) 返回 false,则当可以继续写入数据到流时会触发 'drain' 事件。

来改造我们的代码

const fs = require('fs');
const file = fs.createWriteStream('./big.file');
let timestart = new Date();
function writeBigfile(times) {
  while (times > 0 && file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit. \n')) {
    times--
  }
  if (times  > 0) {
    file.once("drain", () => {
      // 当可以写入的时候再写
      writeBigfile(times--)
    })
  } else {

    file.end();
    console.log(new Date() - timestart);
  }
}
writeBigfile(1e6);