本章节学习流, 流的一个好处在于减少各种异步IO的回调地狱。IO操作遍及我们各种操作,比如数据库读写,文件读写, 文件转换压缩……别的不说,比如第一节,我们要将一个HTML文件返回浏览器,就涉及IO操作。

一个页面,如果按版本划分功能,可能切成一块块给不同的人做,使用fs方法的异步IO方法,可能是这样写的:

fs.asyncXXX(function(err,data){
fs.asyncXXX(function(err,data){
fs.asyncXXX(function(err,data){
fs.asyncXXX(function(err,data){ })
})
})
})

如果使用流,则是这样写

readStream.pipe(transformStream).pipe(writeStream)
a.pipe(b).pipe(c).pipe(d)
//相当于
a.pipe(b);
b.pipe(c);
c.pipe(d);

这是不是与Unix中的管道很相似呢?!无错,它的灵感就在于这!


a | b | c | d

此外,不使用流,如果读出一个很大的文件,则需要将它整个装进内存中,这会很影响性能。使用了流就不用担心这个。

nodejs底层一个提供了4个流, Readable 流、Writable 流、Duplex 流和Transform 流。如果还不能满足你的需求,可以自己继承其个流的基类进行扩展。(例如util.inherits(MyTransform, Transform); )

使用情景 需要重写的方法
只读 Readable _read
只写 Writable _write
双工 Duplex _read, _write
操作被写入数据,然后读出结果 Transform _transform, _flush

说了这么多,我们还没有瞧见一个流。我们来一些例子吧.

可读流:

//aaa.js
var fs = require('fs');
var readStream = fs.createReadStream('myfile.txt');//里面乱写几行
readStream.pipe(process.stdout);

上面是直接读文本的,然后输出到控制台。我们也可以加密一下输出


var crypto = require('crypto');
var fs = require('fs'); var readStream = fs.createReadStream('myfile.txt');
var hash = crypto.createHash('sha1');
readStream
.on('data', function (chunk) {
hash.update(chunk);
})
.on('end', function () {
console.log(hash.digest('hex'));
});

输出一大堆看不懂的密码:

fs模块也有创建可读流的方法:


var fs = require('fs');
var readableStream = fs.createReadStream('file.txt');
var data = ''; readableStream.setEncoding('utf8'); readableStream.on('data', function(chunk) {
data+=chunk;
}); readableStream.on('end', function() {
console.log(data);
});

我们再看一下可读流的各种事件

var fs = require('fs');

var readStream = fs.createReadStream('myfile.txt');
readStream
.on('data', function (chunk) {
console.log("emit data")
console.log(chunk.toString('utf8'))
})
.on('end', function () {
console.log("emit end");
})
.on('close', function () {
console.log("emit close");
})
.on("readable", function(){
console.log("emit readable")
})
.on("error", function(e){
console.log("emit error")
})

再看一下如何重写_read方法:

var Readable = require('stream').Readable;
var util = require('util'); function CountingObjectStream(length, options) {
if (!(this instanceof CountingObjectStream)) {
return new CountingObjectStream(length, options);
}
if (!options) options = {}; // ensure object
options.objectMode = true; // forcing object mode
Readable.call(this, options);
this.lenToCount = length; // how far to count
this.index = 0; // to track our count
}
util.inherits(CountingObjectStream, Readable); CountingObjectStream.prototype._read = function () {
this.index += 1;
if (this.index > this.lenToCount) {
return this.push(null); // done, return
} // pushing number, but could be any non-null obj
this.push(this.index);
}; // consume this stream and output to stdout
// coercing it to a string
var readStream = new CountingObjectStream(10);
readStream
.on('readable', function () {
var obj;
while (null !== (obj = readStream.read())) {
console.log(obj);
}
});

Readable有一个可选的hash参数里,里面有三个配置项:

  • highWaterMark {Number} 停止从底层资源读取前内部缓冲区最多能存放的字节数。缺省为 16kb,对于 objectMode 流则是 16
  • encoding {String} 若给出,则 Buffer 会被解码成所给编码的字符串。缺省为 null
  • objectMode {Boolean} 该流是否应该表现为对象的流。意思是说 stream.read(n) 返回一个单独的对象,而不是大小为 n 的 Buffer

前两个配置项比较易懂,我们看第三个:


var stream = require('stream');
var util = require('util'); function StringifyStream(){
stream.Transform.call(this); this._readableState.objectMode = false;
this._writableState.objectMode = true;
}
util.inherits(StringifyStream, stream.Transform); StringifyStream.prototype._transform = function(obj, encoding, cb){
this.push(JSON.stringify(obj));
cb();
}; var json = require(__dirname + 'test.json');
console.log(json) //这是一个对象
var rs = new stream.Readable({ objectMode: true });
rs.push(json);
rs.push(null); rs.pipe(new StringifyStream()).pipe(process.stdout);

下面是test.json


{
"a":"2",
"b":{
"xxx": 1,
"yyy": false
}
}

可写流

构造器有一个可选配置对象,默认是编码是utf8


var fs = require('fs');
var wstream = fs.createWriteStream('myOutput.txt');
wstream.write('Hello world!\n');
wstream.write('Another line\n');
wstream.end();

我们可以这样改编码


var fs = require('fs');
var wstream = fs.createWriteStream('myOutput.txt');
wstream.write('Hello world!\n');
wstream.write('Another line\n');
wstream.end();

输出二进制文件

var crypto = require('crypto');
var fs = require('fs');
var wstream = fs.createWriteStream('myBinaryFile');
// creates random Buffer of 100 bytes
var buffer = crypto.randomBytes(100);
wstream.write(buffer);
// create another Buffer of 100 bytes and write
wstream.write(crypto.randomBytes(100));
wstream.end();

http://www.sandersdenardi.com/readable-writable-transform-streams-node/
http://www.slideshare.net/shigeki_ohtsu/stream2-kihon
https://cnodejs.org/topic/513ef6cc069911196d0c90a6
http://nodeapi.ucdok.com/#/api/stream.html

http://www.it165.net/pro/html/201406/15924.html
http://calv.info/an-introduction-to-nodes-new-streams/
http://codewinds.com/blog/2013-08-20-nodejs-transform-streams.html

http://stackoverflow.com/questions/20317759/implementing-a-buffered-transform-stream
http://maxogden.com/node-streams.html
http://codewinds.com/blog/2013-08-19-nodejs-writable-streams.html、
http://codewinds.com/blog/2013-08-04-nodejs-readable-streams.html
http://codewinds.com/blog/2013-08-20-nodejs-transform-streams.html

原生nodejs 学习笔记2的更多相关文章

  1. 原生nodejs 学习笔记1

    网上许多nodejs教程或书藉都是教你调用第三方模块来编写nodejs应用的,虽然这是非常便捷的,但是封装太厚,你基本一点东西还是没有学到.人家的模块,人家想怎么改就行,可以下一版本就改了接口,你的应 ...

  2. Nodejs学习笔记(四)——支持Mongodb

    前言:回顾前面零零碎碎写的三篇挂着Nodejs学习笔记的文章,着实有点名不副实,当然,这篇可能还是要继续走着离主线越走越远的路子,从简短的介绍什么是Nodejs,到如何寻找一个可以调试的Nodejs ...

  3. Nodejs学习笔记(三)——一张图看懂Nodejs建站

    前言:一条线,竖着放,如果做不到精进至深,那就旋转90°,至少也图个幅度宽广. 通俗解释上面的胡言乱语:还没学会爬,就学起走了?! 继上篇<Nodejs学习笔记(二)——Eclipse中运行调试 ...

  4. Nodejs学习笔记(二)——Eclipse中运行调试Nodejs

    前篇<Nodejs学习笔记(一)——初识Nodejs>主要介绍了在搭建node环境过程中遇到的小问题以及搭建Eclipse开发Node环境的前提步骤.本篇主要介绍如何在Eclipse中运行 ...

  5. NodeJS学习笔记之Connect中间件模块(一)

    NodeJS学习笔记之Connect中间件模块(一) http://www.jb51.net/article/60430.htm NodeJS学习笔记之Connect中间件模块(二) http://w ...

  6. Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识

    目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...

  7. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  8. Nodejs学习笔记(十六)--- Pomelo介绍&入门

    目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...

  9. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

随机推荐

  1. http报头 Accept 与 Content-Type 的区别

    Accept属于请求头, Content-Type属于实体头. Http报头分为通用报头,请求报头,响应报头和实体报头. 请求方的http报头结构:通用报头|请求报头|实体报头 响应方的http报头结 ...

  2. QString 和char数组转换(转)

    在qt开发过程中经常遇到QString类和char数组进行转换,在此记录一下: QString->char数组 1 2 3 QString str="12fff"; QByt ...

  3. 一个高效的敏感词过滤方法(PHP)

    $badword = array( '张三','张三丰','张三丰田' ); $badword1 = array_combine($badword,array_fill(0,count($badwor ...

  4. 【python】实例-判断用户输入数字的类型

    num=input("please input the num: ") print "the number your input is: "+str(num) ...

  5. Windows 7 With Sp1 简体中文旗舰版

    Windows 7 With Sp1 简体中文旗舰版(MSDN官方原版) 安装Windows 7对于硬件配置的基本要求: •1 GHz 32 位或 64 位处理器 •1 GB 内存(基于32 位)或 ...

  6. dede模块管理一片空白或没有列表内容的解决办法

    为什么dede后台模块管理,打开之后一片空白,又或者没有列表,插件与其他模块的使用也是正常的. 这主要是因为我们在安装模块,然后又卸载模块,卸载的时候选择了删除安装文件,就会出这个问题. 这里面分为两 ...

  7. 禁止Grid、TreeGrid列排序和列菜单

    Ext的Grid和Treegrid默认提供列菜单的功能,在列菜单中可以进行排序以及控制列显示状态. 在实际项目中,往往有些列是不需要用户看到的,因此就必须屏蔽列菜单的功能. 1.屏蔽Grid,包括Ed ...

  8. 大数据挖掘算法篇之K-Means实例

    一.引言 K-Means算法是聚类算法中,应用最为广泛的一种.本文基于欧几里得距离公式:d = sqrt((x1-x2)^+(y1-y2)^)计算二维向量间的距离,作为聚类划分的依据,输入数据为二维数 ...

  9. VBA改写VBA代码

    问题源自:Excel 一个困扰我很长时间的代码转换问题-Word-ExcelHome技术论坛 -  http://club.excelhome.net/thread-1334942-1-1.html ...

  10. Ice框架简介及Vs2013安装Ice 3.7.0步骤及实例

    ICE是什么? ICE是ZEROC官网的开源通信协议产品,它的全称是:The Internet Communications Engine,翻译为中文是互联网通信引擎,是一个面向对象的中间件,支持C+ ...