Ruby's Louvre

每天学习一点点算法

导航

原生nodejs 学习笔记2

本章节学习流, 流的一个好处在于减少各种异步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

posted on 2015-04-15 14:45  司徒正美  阅读(1647)  评论(0编辑  收藏  举报