NodeJS 中Stream 的基本使用

在 NodeJS 中,我们对文件的操作需要依赖核心模块 fs , fs 中有很基本 API 可以帮助我们读写占用内存较小的文件,如果是大文件或内存不确定也可以通过 open 、 read 、 write 、 close 等方法对文件进行操作,但是这样操作文件每一个步骤都要关心,非常繁琐, fs 中提供了可读流和可写流,让我们通过流来操作文件,方便我们对文件的读取和写入。
可读流

1、createReadStream 创建可读流

createReadStream 方法有两个参数,第一个参数是读取文件的路径,第二个参数为 options 选项,其中有八个参数:

r null null 0o666 true 64 * 1024

createReadStream 的返回值为 fs.ReadStream 对象,读取文件的数据在不指定 encoding 时,默认为 Buffer。

let fs = require("fs"); // 创建可读流,读取 1.txt 文件 let rs = fs.creatReadStream("1.txt", { start: 0, end: 3, highWaterMark: 2 });

在创建可读流后默认是不会读取文件内容的,读取文件时,可读流有两种状态,暂停状态和流动状态。

注意:本篇的可写流为流动模式,流动模式中有暂停状态和流动状态,而不是暂停模式,暂停模式是另一种可读流 readable 。

2、流动状态

流动状态的意思是,一旦开始读取文件,会按照 highWaterMark 的值一次一次读取,直到读完为止,就像一个打开的水龙头,水不断的流出,直到流干,需要通过监听 data 事件触发。

假如现在 1.txt 文件中的内容为 0~9 十个数字,我们现在创建可读流并用流动状态读取。

let fs = require("fs"); let rs = fs.createReadStream("1.txt", { start: 0, end: 3, highWaterMark: 2 }); // 读取文件 rs.on("data", data => { console.log(data); }); // 监听读取结束 rs.on("end", () => { console.log("读完了"); }); // <Buffer 30 31> // <Buffer 32 33> // 读完了

在上面代码中,返回的 rs 对象监听了两个事件:

data:每次读取 highWaterMark 个字节,触发一次 data 事件,直到读取完成,回调的参数为每次读取的 Buffer;

end:当读取完成时触发并执行回调函数。

我们希望最后读到的结果是完整的,所以我们需要把每一次读到的结果在 data 事件触发时进行拼接,以前我们可能使用下面这种方式。

let fs = require("fs"); let rs = fs.createReadStream("1.txt", { start: 0, end: 3, highWaterMark: 2 }); let str = ""; rs.on("data", data => { str += data; }); rs.on("end", () => { console.log(str); }); // 0123

在上面代码中如果读取的文件内容是中文,每次读取的 highWaterMark 为两个字节,不能组成一个完整的汉字,在每次读取时进行 += 操作会默认调用 toString 方法,这样会导致最后读取的结果是乱码。

在以后通过流操作文件时,大部分情况下都是在操作 Buffer,所以应该用下面这种方式来获取最后读取到的结果。

let fs = require("fs"); let rs = fs.createReadStream("1.txt", { start: 0, end: 3, highWaterMark: 2 }); // 存储每次读取回来的 Buffer let bufArr = []; rs.on("data", data => { bufArr.push(data); }); rs.on("end", () => { console.log(Buffer.concat(bufArr).toString()); }); // 0123

3、暂停状态

在流动状态中,一旦开始读取文件,会不断的触发 data 事件,直到读完,暂停状态是我们每读取一次就直接暂停,不再继续读取,即不再触发 data 事件,除非我们主动控制继续读取,就像水龙头打开放水一次后马上关上水龙头,下次使用时再打开。

类似于开关水龙头的动作,也就是暂停和恢复读取的动作,在可读流返回的 rs 对象上有两个对应的方法, pause 和 resume 。

在下面的场景中我们把创建可读流的结尾位置更改成 9 ,在每次读两个字节并暂停一秒后恢复读取,直到读完 0~9 十个数字。

let fs = require("fs"); let rs = fs.createReadStream("1.txt", { start: 0, end: 9, hithWaterMark: 2 }); let bufArr = []; rs.on("data", data => { bufArr.push(data); rs.pause(); // 暂停读取 console.log("暂停", new Date()); setTimeout(() => { rs.resume(); // 恢复读取 }, 1000) }); rs.on("end", () => { console.log(Buffer.concat(bufArr).toString()); }); // 暂停 2018-07-03T23:52:52.436Z // 暂停 2018-07-03T23:52:53.439Z // 暂停 2018-07-03T23:52:54.440Z // 暂停 2018-07-03T23:52:55.442Z // 暂停 2018-07-03T23:52:56.443Z // 0123456789

4、错误监听

在通过可读流读取文件时都是异步读取,在异步读取中如果遇到错误也可以通过异步监听到,可读流返回值 rs 对象可以通过 error 事件来监听错误,在读取文件出错时触发回调函数,回调函数参数为 err ,即错误对象。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/d14283f663d8e2034e44f12ea947911c.html