Stream模块是Node.js的一个重要模块。数据流是可读、可写,或即可读又可写的内存结构。
流的目的是提供一种从一个地方向另一个地方传送数据的通用机制。
流一般用于HTTP数据和文件。
Readable流旨在提供一种机制,以方便地读取从其他来源进入应用程序的数据。
常见实例:
Readable流公开了以下事件:
方法 | 说明 |
---|---|
read([size]) | 从流中读取数据。这些数据可以是String、Buffer或者null(null表示没有剩下任何更多的数据)。如果指定size参数,那么被读取的数据将将仅限于那个字节数 |
setEncoding(encoding) | 设置从read()请求读取返回string时使用的编码 |
pause() | 暂停从该对象发出的data事件 |
resume() | 恢复从该事件发出的data事件 |
pipe(desctination, [options]) | 把这个流的输出传输到一个由destination(目的地)指定的Writeable流对象。options是一个Javascript对象。例如,{end: true}当Readable结束时就结束Writable的目的地 |
unpipe([desctination]) | 从Writeable目的地断开这一对象 |
var stream = require('stream') ;
var util = require('util') ;
util.inherits(Answers, stream.Readable) ;
function Answers(opt) {
stream.Readable.call(this, opt) ;
this.quotes = ['yes', 'no', 'maybe'] ;
this._index = 0 ;
}
Answers.prototype._read = function () {
if (this._index > this.quotes.length) {
this.push(null) ;
} else {
this.push( this.quotes[this._index] ) ;
this._index += 1 ;
}
} ;
var r = new Answers() ;
console.log('Direct read: ' + r.read().toString()) ;
r.on('data', function (data) {
console.log('Callback read: ' + data.toString()) ;
}) ;
r.on('end', function (data) {
console.log('No more answers') ;
}) ;
Writeable流旨在提供把数据写入一种可以轻松地在代码的另一个区域被使用的机制。
常用实例:
Writeable流公开的事件:
方法 | 说明 |
---|---|
write(chunk, [encoding], [callback]) | 将数据块写入流对象的数据位置。该数据可以是字符串或缓冲区。如果指定encoding,那么将其用于对字符串数据的编码。如果指定callback,那么它在数据已被刷新后被调用 |
end(chunk, [encoding], [callback]) | 与write()相同,除了它把Writeable对象置于不再接受数据的状态,并发送finish事件外 |
var stream = require('stream') ;
var util = require('util') ;
util.inherits(Writer, stream.Writable) ;
function Writer(opt) {
stream.Writable.call(this, opt) ;
this.data = new Array() ;
}
Writer.prototype._write = function (data, encoding, callback) {
this.data.push( data.toString('utf8') ) ;
console.log('Adding: ' + data) ;
callback() ;
} ;
var w = new Writer() ;
for(var i = 1; i <= 5; i++ ) {
w.write('Item' + i, 'utf8') ;
}
w.end('ItemLast') ;
console.log(w.data) ;
Duplex(双向)流是结合可读写功能的流。
var stream = require('stream') ;
var util = require('util') ;
util.inherits(Duplexer, stream.Duplex) ;
function Duplexer(opt) {
stream.Duplex.call(this, opt) ;
this.data = [] ;
}
Duplexer.prototype._read = function readItem(size) {
var chunk = this.data.shift() ;
if (chunk == 'stop') {
this.push(null) ;
} else {
if (chunk) {
this.push(chunk) ;
} else {
setTimeout(readItem.bind(this), 500, size) ;
}
}
} ;
Duplexer.prototype._write = function (data, encoding, callback) {
this.data.push(data) ;
callback() ;
} ;
var d = new Duplexer() ;
d.on('data', function (chunk) {
console.log('read: ', chunk.toString()) ;
}) ;
d.on('end', function () {
console.log('Message Complete') ;
}) ;
d.write('I think, ') ;
d.write('therefore ') ;
d.write('I am, ') ;
d.write('Rene descartes') ;
d.write('stop') ;
Transform(变换)流扩展了Duplex流,但它修改Writeable流和Readable流之间的数据。当你需要修改从一个系统到另一个系统的数据时,此流会非常有用。
Duplex和Transform流之间的一个主要区别是:在Transform流中不需要实现_read()和_write()原型方法。这些被作为直通函数提供。相反,你要实现_transform(chunk, encoding, callback)和_finish(callback)方法。此_transform()方法应该接受来自write()请求的数据,对其修改,并推出修改后的数据。
var stream = require('stream') ;
var util = require('util') ;
util.inherits(JSONObjectStream, stream.Transform) ;
function JSONObjectStream(opt) {
stream.Transform.call(this, opt) ;
}
JSONObjectStream.prototype._transform = function (data, encoding, callback) {
object = data ? JSON.parse(data.toString()) : '' ;
this.emit('object', object) ;
object.handled = true ;
this.push(JSON.stringify(object)) ;
callback() ;
} ;
JSONObjectStream.prototype._finish = function (cb) {
cb() ;
} ;
var tc = new JSONObjectStream() ;
tc.on('object', function (object) {
console.log('Name: %s', object.name) ;
console.log('Color: %s', object.color) ;
}) ;
tc.on('data', function (data) {
console.log('Data: %s', data.toString()) ;
}) ;
tc.write('{"name": "Carolinus", "color": "Green"}') ;
tc.write('{"name": "Solarius", "color": "Blue"}') ;
tc.write('{"name": "Lo Tea Zhao", "color": "Gold"}') ;
tc.write('{"name": "Ommadon", "color": "Red"}') ;
可以用流对象做的最酷的东西之一是通过pipe(writeableStream, [options])函数把Readable流链接到Writeable流。
var stream = require( 'stream' ) ;
var util = require( 'util' ) ;
util.inherits( Reader, stream.Readable ) ;
util.inherits( Writer, stream.Writable ) ;
function Reader(opt) {
stream.Readable.call(this, opt) ;
this._index = 1 ;
}
Reader.prototype._read = function (size) {
var i = this._index++ ;
if( i > 10 ) {
this.push(null) ;
} else {
this.push('Item ' + i.toString()) ;
}
} ;
function Writer(opt) {
stream.Writable.call(this, opt) ;
this._index = 1 ;
}
Writer.prototype._write = function (data, encoding, callback) {
console.log(data.toString()) ;
callback() ;
} ;
var r = new Reader() ;
var w = new Writer() ;
r.pipe( w ) ;
支持如下这些压缩算法:
- gzip/gunzip: 标准gzip压缩
- deflate/inflate: 基于Huffman编码的标准deflate压缩算法
- deflateRaw/inflateRaw:针对原始缓冲区的deflate压缩算法
var zlib = require('zlib') ;
var input = '......................text.....................' ;
zlib.deflate(input, function (err, buffer) {
if(!err) {
console.log('deflate (%s): ', buffer.length, buffer.toString('base64')) ;
zlib.inflate(buffer, function (err, buffer) {
if(!err) {
console.log('inflate (%s): ', buffer.length, buffer.toString()) ;
}
}) ;
zlib.unzip(buffer, function (err, buffer) {
if (!err) {
console.log('unzip deflate (%s): ', buffer.length, buffer.toString()) ;
}
}) ;
}
}) ;
zlib.deflateRaw(input, function (err, buffer) {
if(!err) {
console.log('deflateRaw (%s): ', buffer.length, buffer.toString('base64')) ;
zlib.inflateRaw(buffer, function (err, buffer) {
console.log('inflateRaw (%s): ', buffer.length, buffer.toString()) ;
}) ;
}
}) ;
zlib.gzip(input, function (err, buffer) {
if(!err) {
console.log('gzip (%s): ', buffer.length, buffer.toString('base64')) ;
zlib.gunzip(buffer, function (err, buffer) {
if(!err) {
console.log('gunzip (%s): ', buffer.length, buffer.toString()) ;
}
}) ;
zlib.unzip(buffer, function (err, buffer) {
if(!err) {
console.log('unzip gzip (%s): ', buffer.length, buffer.toString()) ;
}
}) ;
}
}) ;
var zlib = require('zlib') ;
var gzip = zlib.createGzip() ;
var fs = require('fs') ;
var inFile = fs.createReadStream('zlib_file.js') ;
var outFile = fs.createWriteStream('zlib_file.gz') ;
inFile.pipe(gzip).pipe(outFile) ;
setTimeout(function () {
var gunzip = zlib.createUnzip({flush: zlib.Z_FULL_FLUSH}) ;
var inFile = fs.createReadStream('zlib_file.gz') ;
var outFile = fs.createWriteStream('zlib_file.unzipped') ;
inFile.pipe(gunzip).pipe(outFile) ;
}, 3000) ;