Node.js API实例讲解——读取流
读取流
通过这种方式我们可以创建一个Readable实例。
// 这样创建不会抛出任何异常 var reader = new Readable(); // 打印一下这个对象有什么吧 console.log(reader);
打印结果:
{ _readableState: { highWaterMark: 16384, buffer: [], length: 0, pipes: null, pipesCount: 0, flowing: false, ended: false, endEmitted: false, reading: false, calledRead: false, sync: true, needReadable: false, emittedReadable: false, objectMode: false, ranOut: false, awaitDrain: 0, readingMore: false, decoder: null }, readable: true, domain: null, _events: {}, _maxListeners: 10 }
哇!东西可真多!
看到_readableState对象内部的属性了吗?这是表示readable内部状态的,当然这是私有的,根据“某某不平等条约”不应该更改这些属性。不过可以通过合法的方式设置highWaterMark、encoding和objectMode三个属性。
通过给构造函数传值
var Readable = require("stream").Readable; var reader = new Readable({ objectMode:true, encoding:"utf16le", highWaterMark:1024*10}); console.log(reader);
打印部分结果:
{ _readableState: { highWaterMark: 10240, objectMode: true, decoder: { encoding: 'utf16le' } }
会发现确实改变了这三个属性,其余属性都无法更改,属于内部属性了。
encoding属性默认为null。用于Buffer编码所用。
highWaterMark属性默认是16KB,相当于一个量杯,从底层读取数据保存在内部一个“小桶”里,当小桶装满后或底层资源就这么多时,就会返回(读取)一桶数据,如果底层还有数据,那么就继续装桶,这个桶的最大值就是highWaterMark定义的。当然这个依赖实现类,实现类实现_read方法。
objectMode属性默认为false,表示调用read(size)时,根据size大小返回底层数据;objectMode=true 时,size参数失效,会返回内部储存的数据。(这个不太好理解,后面会用例子讲解清楚)。
OK,实现一个Readable类,代码如下:
var Readable = require("stream").Readable; function TestRead(data,options){ this._buf = data; this._po = 0; Readable.call(this,options); } TestRead.prototype = Object.create(Readable.prototype,{ constructor:{value:TestRead}, _read:{value:function(n){ if(this._po >= this._buf.length){ this.push(null); }else{ var ct = this._buf.slice(this._po,this._po+n>this._buf.length?this._buf.length:this._po+n); this.push(ct); } this._po += n; } } })
测试这个Readable子类TestReadsex
// 要读取的数据 var buf = new Buffer([ 0x12,0x22, 0x32,0x11, 0x22,0x33, 0x23,0x23, 0x44,0x22, 0x34,0x56, 0x91,0x90, 0x43]); // 实例化个对象 var read = new TestRead(buf,{highWaterMark:2}); console.log(read.read(2)) console.log(read.read(1)) console.log(read.read(3))
运行结果
<Buffer 12 22> <Buffer 32> <Buffer 11 22 33>
通过这个结果,可以看出,默认objectMode = false时,那么read(size)可以访问byte数量。如果size越除了底层数据,那么返回null。
当 objectMode = true,看例子结果。
// 实例化个对象 var read = new TestRead(buf,{highWaterMark:2,objectMode:true}); console.log(read.read(2)) console.log(read.read(1)) console.log(read.read(3))
打印结果是:
<Buffer 12 22> <Buffer 32 11> <Buffer 22 33>
这说明,每次read返回的是内部储存的内容,而size失效。
readable._read(size)
这应该是实现子类调用的方法,不应该由外界调用。所有Readable子类必须通过实现这个方法,达到从底层读取数据的功能。
size参数是个参考参数,这个参数就是 highWaterMark定义的;这个参数的含义,是每次从底层数据读取的最大byte数量。每次调用都应该底层读取一段新的数据,读取到数据后,通过this.push(data)方法推送到读队列。(参看前面的例子)
readable.push(data)
data可以是Buffer|String|NULL,方法返回Boolean值,当push(false)时,就会retrun false,否则true。
这个方法应该是由_read方法内部调用的,当第一次push数据到读队列,内部会触发“readable”事件,当push(null) 时,内部会触发end
事件。
readable.unshift(data)
顾名思义,就是push方法的反方向,也就是插入到数据块前方。其余功能和特性与push一样。
readable.setEncoding(encoding)
这个是设置编码的,底层数据返回的是String类型的数据时有效,也可以在构造函数设置,前面已提到。
readable.read([size])
前面根据实例介绍了read方法,read方法和_read方法不是一个概念,要分开理解。
fuck调用 read之后才会激发 "readable" 事件。有几种情况:
当objectMode = true时, read(0)将返回null,没什么现象和作用,read()会返回当前缓冲区数据,并刷新缓冲区。read(2)或read(18)效果和read()效果一致。
当objectMode = false时,read(0)将返回null,并且缓冲区不会刷新,会把下一次的push数据和缓冲区的数据合并。read()会返回当前缓冲区数据,并刷新缓冲区,这个和objectMode=true时的情况一致。read(n) 情况比较复杂,第一种情况:n大小在原始数据范围内时,会返回相应多的数据,并且会把highWaterMark变成n大小,如果再调用read(n2)一次,那么highWaterMark会再次增大。第二种情况,n大小超出原始数据范围时,会返回null,hightWaterMark和第一种情况一样增大,但不会消耗数据,这种情况下data事件会直接获得全部数据。
readable.pause();
暂停流读取。并执行一次 readable.read(),注意是不参数read()。
readable.resume();
恢复流读取。并执行一次 readable.read()。