浅析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 相关文章推荐
JS 密码强度验证(兼容IE,火狐,谷歌)
Mar 15 Javascript
javascript编写实用的省市选择器
Feb 12 Javascript
深入理解JavaScript中的call、apply、bind方法的区别
May 30 Javascript
很棒的js Tab选项卡切换效果
Aug 30 Javascript
详解Vue-cli 创建的项目如何跨域请求
May 18 Javascript
BootStrap点击保存后实现模态框自动关闭的思路(模态框)
Sep 26 Javascript
使用FileReader API创建Vue文件阅读器组件
Apr 03 Javascript
vue.js使用3DES加密的方法示例
May 18 Javascript
Node.js一行代码实现静态文件服务器的方法步骤
May 07 Javascript
vue中keep-alive内置组件缓存的实例代码
Apr 16 Javascript
vue element table中自定义一些input的验证操作
Jul 18 Javascript
PHP读取远程txt文档到数组并实现遍历
Aug 25 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
如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量的
2007/03/06 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
2014/12/24 PHP
php中简单的对称加密算法实现
2017/01/05 PHP
PHP操作redis实现的分页列表,新增,删除功能封装类与用法示例
2018/08/04 PHP
jQuery 瀑布流 绝对定位布局(二)(延迟AJAX加载图片)
2012/05/23 Javascript
浅谈Javascript事件模拟
2012/06/27 Javascript
jquery入门—编写一个导航条(可伸缩)
2013/01/07 Javascript
jquery 提交值不为空的元素示例代码
2013/05/10 Javascript
理解javascript中的Function.prototype.bind的方法
2017/02/03 Javascript
selenium 与 chrome 进行qq登录并发邮件操作实例详解
2017/04/06 Javascript
jQuery实现点击DIV同时点击CheckBox,并为DIV上背景色的实例
2017/12/18 jQuery
javascript与PHP动态往类中添加方法对比
2018/03/21 Javascript
Vue源码学习之关于对Array的数据侦听实现
2019/04/23 Javascript
深入解析vue 源码目录及构建过程分析
2019/04/24 Javascript
浅谈vue限制文本框输入数字的正确姿势
2019/09/02 Javascript
javascript实现异形滚动轮播
2019/11/28 Javascript
vue 使用class创建和清除水印的示例代码
2020/12/25 Vue.js
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
Python程序中使用SQLAlchemy时出现乱码的解决方案
2015/04/24 Python
Python多进程并发(multiprocessing)用法实例详解
2015/06/02 Python
python通过ssh-powershell监控windows的方法
2015/06/02 Python
Python发送邮件功能示例【使用QQ邮箱】
2018/12/04 Python
python实现的分层随机抽样案例
2020/02/25 Python
python中shell执行知识点
2020/05/06 Python
IE8下CSS3选择器nth-child() 不兼容问题的解决方法
2016/11/16 HTML / CSS
比利时家具购买网站:Home24
2019/01/03 全球购物
Dogeared官网:在美国手工制作的珠宝
2019/08/24 全球购物
机械专业毕业生自荐信
2013/11/02 职场文书
志愿者活动总结范文
2014/04/26 职场文书
一份教室追逐打闹的检讨书
2014/09/27 职场文书
列车乘务员工作不细心检讨书
2014/10/07 职场文书
保密工作整改情况汇报
2014/11/06 职场文书
python基础学习之生成器与文件系统知识总结
2021/05/25 Python
golang生成并解析JSON
2022/04/14 Golang
Vue OpenLayer测距功能的实现
2022/04/20 Vue.js
Python中的协程(Coroutine)操作模块(greenlet、gevent)
2022/05/30 Python