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