微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

NodeJS 中Stream 的基本使用

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

1、createReadStream 创建可读流

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

rush:js;"> r null null 0o666 true 64 * 1024

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

rush:js;"> 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 十个数字,我们现在创建可读流并用流动状态读取。

{ console.log(data); }); // 监听读取结束 rs.on("end",() => { console.log("读完了"); }); // // // 读完了

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

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

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

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

{ str += data; }); rs.on("end",() => { console.log(str); }); // 0123

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

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

{ bufArr.push(data); }); rs.on("end",() => { console.log(Buffer.concat(bufArr).toString()); }); // 0123

3、暂停状态

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

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

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

{ 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 ,即错误对象。

{ bufArr.push(data); }); rs.on("err",err => { console.log(err); }); rs.on("end",() => { console.log(Buffer.concat(bufArr).toString()); }); // { Error: ENOENT: no such file or directory,open '......xxx.js' ......}

5、打开和关闭文件的监听

流的适用性非常广,不只是文件读写,也可以用在 http 中数据的请求和响应上,但是在针对文件读取返回的 rs 上有两个专有的事件用来监听文件的打开与关闭

open 事件用来监听文件的打开,回调函数在打开文件后执行, close 事件用来监听文件关闭,如果创建的可读流的 autoClose 为 true ,在自动关闭文件时触发,回调函数关闭文件后执行

{ console.log("open"); }); rs.on("close",() => { console.log("close"); }); // open

在上面代码我们看出只要创建了可读流就会打开文件触发 open 事件,因为认为暂停状态,没有对文件进行读取,所以不会关闭文件,即不会触发 close 事件。

{ console.log("open"); }); rs.on("data",data => { console.log(data); }); rs.on("end",() => { console.log("end"); }); rs.on("close",() => { console.log("close"); }); // open // // // end // close

从上面例子执行的打印结果可以看出只有开始读取文件并读完后,才会关闭文件并触发 close 事件, end 事件的触发要早于 close 。

可写流

1、createWriteStream 创建可写流

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

rush:js;"> w utf8 null 0o666 true 16 * 1024 createWriteStream 返回值为 fs.WriteStream 对象,第一次写入时会真的写入文件中,继续写入,会写入到缓存中。 let fs = require("fs"); // 创建可写流,写入 2.txt 文件 let ws = fs.createWriteStream("2.txt",highWaterMark: 3 });

2、可写流的 write 方法

在可写流中将内容写入文件需要使用 ws 的 write 方法,参数为写入的内容,返回值是一个布尔值,代表 highWaterMark 的值是否足够当前的写入,如果足够,返回 true ,否则返回 false ,换种说法就是写入内容的长度是否超出了 highWaterMark ,超出返回 false 。

rush:js;"> let fs = require("fs"); let ws = fs.createWriteSteam("2.txt",highWaterMark: 3 }); let flag1 = ws.write("1"); console.log(flag1); let flag2 = ws.write("2"); console.log(flag2); let flag3 = ws.write("3"); console.log(flag3); // true // true // false

写入不存在的文件时会自动创建文件,如果 start 的值不是 0 ,在写入不存在的文件认找不到写入的位置。

3、可写流的 drain 事件

drain 意为 “吸干”,当前写入的内容已经大于等于了 highWaterMark ,会触发 drain 事件,当内容全部从缓存写入文件后,会执行回调函数

{ console.log("吸干"); }); // true // true // false

4、可写流的 end 方法

end 方法传入的参数为最后写入的内容, end 会将缓存未写入的内容清空写入文件,并关闭文件

{ console.log("吸干"); }); ws.end("写完了"); // true // true // false

调用 end 方法后,即使再次写入的值超出了 highWaterMark 也不会再触发 drain 事件了,此时打开 2.txt 后发现文件中的内容为 "123写完了"。

rush:js;"> let fs = require("fs"); let ws = fs.createWriteStream("2.txt",highWaterMark: 3 }); ws.write("1"); ws.end("写完了"); ws.write("2"); // Error [ERR_STREAM_WRITE_AFTER_END]: write after end...

调用 end 方法后,不可以再调用 write 方法写入,否则会报一个很常见的错误 write after end ,文件原有内容会被清空,而且不会被写入新内容

可写流与可读流混合使用

可写流和可读流一般配合来使用,读来的内容如果超出了可写流的 highWaterMark ,则调用可读流的 pause 暂停读取,等待内存中的内容写入文件,未写入的内容小于 highWaterMark 时,调用可写流的 resume 恢复读取,创建可写流返回值的 rs 上的 pipe 方法是专门用来连接可读流和可写流的,可以将一个文件读来的内容通过流写到另一个文件中。

rush:js;"> let fs = require("pipe"); // 创建可读流和可写流 let rs = fs.createReadStream("1.txt",{ highWaterMark: 3 }); let ws = fs.createWriteStream("2.txt",{ highWaterMark: 2 }); // 将 1.txt 的内容通过流写入 2.txt 中 rs.pipe(ws);

通过上面的这种类似于管道的方式,将一个流从一个文件输送到了另一个文件中,而且会根据读流和写流的 highWaterMark 自由的控制写入的 “节奏”,不用担心内存的消耗。

总结

以上所述是小编给大家介绍的NodeJS 中Stream 的基本使用。编程之家 jb51.cc 收集整理的教程希望能对你有所帮助,如果觉得编程之家不错,可分享给好友!感谢支持

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐