浅析Node.js 中 Stream API 的使用


Posted in Javascript onOctober 23, 2015

本文由浅入深给大家介绍node.js stream api,具体详情请看下文吧。

基本介绍

在 Node.js 中,读取文件的方式有两种,一种是用 fs.readFile ,另外一种是利用 fs.createReadStream 来读取。

fs.readFile 对于每个 Node.js 使用者来说最熟悉不过了,简单易懂,很好上手。但它的缺点是会先将数据全部读入内存,一旦遇到大文件的时候,这种方式读取的效率就非常低下了。

而 fs.createReadStream 则是通过 Stream 来读取数据,它会把文件(数据)分割成小块,然后触发一些特定的事件,我们可以监听这些事件,编写特定的处理函数。这种方式相对上面来说,并不好上手,但它效率非常高。

事实上, Stream 在 Node.js 中并非仅仅用在文件处理上,其他地方也可以看到它的身影,如 process.stdin/stdout , http , tcp sockets , zlib , crypto 等都有用到。

本文是我学习 Node.js 中的 Stream API 中的一点总结,希望对大家有用。

特点

基于事件通讯

可以通过 pipe 来连接流

种类

Readable Stream 可读数据流

Writeable Stream 可写数据流

Duplex Stream 双向数据流,可以同时读和写

Transform Stream 转换数据流,可读可写,同时可以转换(处理)数据

事件

可读数据流的事件

readable 数据向外流时触发

data 对于那些没有显式暂停的数据流,添加data事件监听函数,会将数据流切换到流动态,尽快向外提供数据

end 读取完数据时触发。注意不能和 writeableStream.end() 混淆,writeableStream 并没有 end 事件,只有 .end() 方法

close 数据源关闭时触发

error 读取数据发生错误时触发

可写数据流的事件

drain writable.write(chunk) 返回 false 之后,缓存全部写入完成,可以重新写入时就会触发

finish 调用 .end 方法时,所有缓存的数据释放后触发,类似于可读数据流中的 end 事件,表示写入过程结束

pipe 作为 pipe 目标时触发

unpipe 作为 unpipe 目标时触发

error 写入数据发生错误时触发

状态

可读数据流有两种状态: 流动态 和 暂停态 ,改变数据流状态的方法如下:

暂停态 -> 流动态

添加 data 事件的监听函数

调用 resume 方法

调用 pipe 方法

注意:如果转为流动态时,没有 data 事件的监听函数,也没有 pipe 方法的目的地,那么数据将遗失。

流动态 -> 暂停态

不存在 pipe 方法的目的地时,调用 pause 方法

存在 pipe 方法的目的地时,移除所有 data 事件的监听函数,并且调用 unpipe 方法,移除所有 pipe 方法的目的地

注意:只移除 data 事件的监听函数,并不会自动引发数据流进入「暂停态」。另外,存在 pipe 方法的目的地时,调用 pause 方法,并不能保证数据流总是处于暂停态,一旦那些目的地发出数据请求,数据流有可能会继续提供数据。

用法

读写文件

var fs = require('fs');
// 新建可读数据流
var rs = fs.createReadStream('./test1.txt');
// 新建可写数据流
var ws = fs.createWriteStream('./test2.txt');
// 监听可读数据流结束事件
rs.on('end', function() {
 console.log('read text1.txt successfully!');
});
// 监听可写数据流结束事件
ws.on('finish', function() {
 console.log('write text2.txt successfully!');
});
// 把可读数据流转换成流动态,流进可写数据流中
rs.pipe(ws);
读取 CSV 文件,并上传数据(我在生产环境中写过)
var fs = require('fs');
var es = require('event-stream');
var csv = require('csv');
var parser = csv.parse();
var transformer = csv.transform(function(record) {
 return record.join(',');
});
var data = fs.createReadStream('./demo.csv');
data
 .pipe(parser)
 .pipe(transformer)
 // 处理前一个 stream 传递过来的数据
 .pipe(es.map(function(data, callback) {
  upload(data, function(err) {
   callback(err);
  });
 }))
 // 相当于监听前一个 stream 的 end 事件
 .pipe(es.wait(function(err, body) {
  process.stdout.write('done!');
 }));

更多用法

可以参考一下 https://github.com/jeresig/node-stream-playground ,进去示例网站之后直接点 add stream 就能看到结果了。

常见坑

用 rs.pipe(ws) 的方式来写文件并不是把 rs 的内容 append 到 ws 后面,而是直接用 rs 的内容覆盖 ws 原有的内容

已结束/关闭的流不能重复使用,必须重新创建数据流

pipe 方法返回的是目标数据流,如 a.pipe(b) 返回的是 b,因此监听事件的时候请注意你监听的对象是否正确

如果你要监听多个数据流,同时你又使用了 pipe 方法来串联数据流的话,你就要写成:

data

.on('end', function() {
 console.log('data end');
})
.pipe(a)
.on('end', function() {
 console.log('a end');
})
.pipe(b)
.on('end', function() {
 console.log('b end');
});

常用类库

event-stream 用起来有函数式编程的感觉,个人比较喜欢

awesome-nodejs#streams 由于其他 stream 库我都没用过,所以有需求的就直接看这里吧

以上内容是小编给大家介绍的Node.js 中 Stream API 的使用,希望大家喜欢。

Javascript 相关文章推荐
Javascript匿名函数的一种应用 代码封装
Jun 27 Javascript
利用javascript判断文件是否存在
Dec 31 Javascript
jquery操作下拉列表、文本框、复选框、单选框集合(收藏)
Jan 08 Javascript
利用jQuery实现可以编辑的表格
May 26 Javascript
JS运动框架之分享侧边栏动画实例
Mar 03 Javascript
JS建造者模式基本用法实例分析
Jun 30 Javascript
javascript实现超炫的向上滑行菜单实例
Aug 03 Javascript
javascript的 {} 语句块详解
Feb 27 Javascript
js select下拉联动 更具级联性!
Apr 17 Javascript
在vue-cli项目中使用bootstrap的方法示例
Apr 21 Javascript
JS动画实现回调地狱promise的实例代码详解
Nov 08 Javascript
微信小程序swiper实现滑动放大缩小效果
Nov 15 Javascript
jQuery实现右侧显示可向左滑动展示的深色QQ客服效果代码
Oct 23 #Javascript
js显示当前日期时间和星期几
Oct 22 #Javascript
js检测用户输入密码强度
Oct 22 #Javascript
使用JQuery实现Ctrl+Enter提交表单的方法
Oct 22 #Javascript
实例详解angularjs和ajax的结合使用
Oct 22 #Javascript
jQuery多级手风琴菜单实例讲解
Oct 22 #Javascript
使用jquery插件qrcode生成二维码
Oct 22 #Javascript
You might like
全国FM电台频率大全 - 7 吉林省
2020/03/11 无线电
10个简化PHP开发的工具
2014/12/25 PHP
浅析php静态方法与非静态方法的用法区别
2016/05/17 PHP
Yii2.0预定义的别名功能小结
2016/07/04 PHP
PHP中使用jQuery+Ajax实现分页查询多功能操作(示例讲解)
2017/09/17 PHP
thinkphp5 migrate数据库迁移工具
2018/02/20 PHP
Laravel框架Request、Response及Session操作示例
2019/05/06 PHP
Thinkphp5.0框架视图view的循环标签用法示例
2019/10/12 PHP
jquery ztree实现下拉树形框使用到了json数据
2014/05/14 Javascript
详解JavaScript中getFullYear()方法的使用
2015/06/10 Javascript
基于jQuery滑动杆实现购买日期选择效果
2015/09/15 Javascript
jQuery旋转木马式幻灯片轮播特效
2015/12/04 Javascript
Jquery给当前页或者跳转后页面的导航栏添加选中后样式的实例
2016/12/08 Javascript
AngularJS常见过滤器用法实例总结
2017/07/06 Javascript
HTML5开发Kinect体感游戏的实例应用
2017/09/18 Javascript
微信小程序滑动选择器的实现代码
2018/08/10 Javascript
在小程序开发中使用npm的方法
2018/10/17 Javascript
微信小程序实现日历功能
2018/11/27 Javascript
JavaScript实现轮播图特效
2020/04/10 Javascript
Python导入oracle数据的方法
2015/07/10 Python
初步认识Python中的列表与位运算符
2015/10/12 Python
python调用百度语音识别api
2018/08/30 Python
Python3按一定数据位数格式处理bin文件的方法
2019/01/24 Python
python数据持久存储 pickle模块的基本使用方法解析
2019/08/30 Python
pyinstaller打包程序exe踩过的坑
2019/11/19 Python
关于pytorch处理类别不平衡的问题
2019/12/31 Python
python读取ini配置的类封装代码实例
2020/01/08 Python
python实现快递价格查询系统
2020/03/03 Python
Python telnet登陆功能实现代码
2020/04/16 Python
Python OpenCV实现测量图片物体宽度
2020/05/27 Python
Python爬虫requests库多种用法实例
2020/05/28 Python
意大利中国电子产品购物网站:Geekmall.com
2019/09/30 全球购物
vue项目实现分页效果
2021/03/24 Vue.js
领导干部群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
庆祝国庆节标语
2014/10/09 职场文书
Python Parser的用法
2021/05/12 Python