Node.Js Stream(流)-(一)

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。

Node.js,Stream 有四种流类型:

  • Readable - 可读操作。   Writable - 可写操作。   Duplex - 可读可写操作.   Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  • data - 当有数据可读时触发。  end - 没有更多的数据可读时触发。  error - 在接收和写入过程中发生错误时触发。  finish - 所有数据已被写入到底层系统时触发。

Readable 读取流

Readable流提供了一种将外部来源(比如文件、套接字等)的数据读入到应用程序的机制。

可读的流有两种模式:流动模式和暂停模式。流动模式下,数据会自动从来源流出,跟不老泉似的,直到来源的数据耗尽。暂停模式下,你得通过stream.read()主动去要数据,你要了它才从来源读,你不要它就在那儿耗着等你。

可读流在创建时都是暂停模式。暂停模式和流动模式可以互相转换。

要从暂停模式切换到流动模式,有下面三种办法:

  • 给“data”事件关联了一个处理器
  • 显式调用resume()
  • 调用pipe()将可读流桥接到一个可写流上

要从流动模式切换到暂停模式,有两种途径:

  • 如果这个可读的流没有桥接可写流组成管道,直接调用pause()
  • 如果这个可读的流与若干可写流组成了管道,需要移除与“data”事件关联的所有处理器,并且调用unpipe()方法断开所有管道。

需要注意的是,出于向后兼容的原因,移除“data”事件的处理器,可读流并不会自动从流动模式转换到暂停模式;还有,对于已组成管道的可读流,调用pause也不能保证这个流会转换到暂停模式。

Readable流的一些常见实例如下:

  • 客户端的HTTP响应
  • 服务端的HTTP请求
  • fs读取流
  • zlib流
  • crypto(加密)流
  • TCP套接字
  • 子进程的stdout和stderr
  • process.stdin

Readable流提供了以下事件

  • readable:在数据块可以从流中读取的时候发出。它对应的处理器没有参数,可以在处理器里调用read([size])方法读取数据。
  • data:有数据可读时发出。它对应的处理器有一个参数,代表数据。如果你只想快快地读取一个流的数据,给data关联一个处理器是最方便的办法。处理器的参数是Buffer对象,如果你调用了Readable的setEncoding(encoding)方法,处理器的参数就是String对象。
  • end:当数据被读完时发出。对应的处理器没有参数。
  • close:当底层的资源,如文件,已关闭时发出。不是所有的Readable流都会发出这个事件。对应的处理器没有参数。
  • error:当在接收数据中出现错误时发出。对应的处理器参数是Error的实例,它的message属性描述了错误原因,stack属性保存了发生错误时的堆栈信息。

Readable还提供了一些函数,我们可以用它们读取或操作流

  • read([size]):如果你给read方法传递了一个大小作为参数,那它会返回指定数量的数据,如果数据不足,就会返回null。如果你不给read方法传参,它会返回内部缓冲区里的所有数据,如果没有数据,会返回null,此时有可能说明遇到了文件末尾。read返回的数据可能是Buffer对象,也可能是String对象。
  • setEncoding(encoding):给流设置一个编码格式,用于解码读到的数据。调用此方法后,read([size])方法返回String对象。
  • pause():暂停可读流,不再发出data事件
  • resume():恢复可读流,继续发出data事件
  • pipe(destination,[options]):把这个可读流的输出传递给destination指定的Writable流,两个流组成一个管道。options是一个JS对象,这个对象有一个布尔类型的end属性,默认值为true,当end为true时,Readable结束时自动结束Writable。注意,我们可以把一个Readable与若干Writable连在一起,组成多个管道,每一个Writable都能得到同样的数据。这个方法返回destination,如果destination本身又是Readable流,就可以级联调用pipe(比如我们在使用gzip压缩、解压缩时就会这样,马上会讲到)。
  • unpipe([destination]):端口与指定destination的管道。不传递destination时,断开与这个可读流连在一起的所有管道。
  • isPaused() :判断是否已经是

好吧,大概就这些了,我们来举一个简单的使用Readable的例子。fs模块为例吧

fs.ReadStream实现了stream.Readable,另外还提供了一个“open”事件,你可以给这个事件关联处理器,处理器的参数是文件描述符(一个整型数)。

fs.createReadStream(path[, options])用来打开一个可读的文件流,它返回一个fs.ReadStream对象。path参数指定文件的路径,可选的options是一个JS对象,可以指定一些选项,类似下面这样:

options 参数说明
{
  flags: 'r',		  //指定用什么模式打开文件,’w’代表写,’r’代表读,类似的还有’r+’、’w+’、’a’等
  encoding: null,     //默认就是“utf8”,你还可以为它指定”ascii”或”base64”
  fd: null,			  //当你指定了这个属性时,createReadableStream会根据传入的fd创建一个流,忽略path。
  mode: 0o666,
  autoClose: true,   //当发生错误或文件读取结束时会自动关闭文件描述符。
  highWaterMark :64,//要知道,不同的设置为一个可读流highwatermark默认值(16 KB),此方法返回的流具有相同的参数的默认值64 KB。
  start:0,
  end:0  //选项可以包括开始和结束值,从文件中读取一个字节范围,而不是整个文件。开始和结束都是包容性的,并开始在0。该编码可以是任何一个接受的缓冲区。
}
实例一:
var fs=require('fs');
var path=require('path');
var file=path.resolve('/test1/one.txt');
//指定开始位置,结束位置读取文件
var readable=fs.createReadStream(file,{
	highWaterMark:6,
	start:6,
	end:12
});
readable.on('open',function(fd){
	console.log('打开文件成功,句柄:'+fd); // 打开文件成功,句柄:3
});
readable.on('data',function(chunk){
	console.info(chunk.toString('ascii')); //789012
});
readable.on('end',function(){
	console.log('读取结束');  //读取结束
});
readable.on('close',()=>{
	console.log('读取关闭');  //读取关闭
});
readable.on('error',function(err){
	console.log('读取异常,'+err); //当出现异常时触发
});

Writable 写入流

Writable流提供了一个接口,用来把数据写入到目的设备(或内存)中。Writable流的一些常见实例:

  • 客户端的HTTP请求
  • 服务器的HTTP响应
  • fs写入流
  • zlib流
  • crypto(加密)流
  • TCP套结字
  • 子进程的stdin
  • process.stdout和process.stderr

Writable流的write(chunk[,encoding] [,callback])方法可以把数据写入流中。其中,chunk是待写入的数据,是Buffer或String对象。这个参数是必须的,其它参数都是可选的。如果chunk是String对象,encoding可以用来指定字符串的编码格式,write会根据编码格式将chunk解码成字节流再来写入。callback是数据完全刷新到流中时会执行的回调函数。write方法返回布尔值,当数据被完全处理后返回true(不一定是完全写入设备哦)。

Writable流的end([chunk] [,encoding] [,callback])方法可以用来结束一个可写流。它的三个参数都是可选的。chunk和encoding的含义与write方法类似。callback是一个可选的回调,当你提供它时,它会被关联到Writable的finish事件上,这样当finish事件发射时它就会被调用。

Writable还有setDefaultEncoding等方法,具体可以参考在线文档。

现在我们来看看Writable公开的事件

  • finish: 在end()被调用、所有数据都已被写入底层设备后发射。对应的处理器函数没有参数。
  • drain: 在write()方法,写入文件缓存清空时触发
  • pipe: 当你在Readable流上调用pipe()方法时,Writable流会发射这个事件,对应的处理器函数有一个参数,类型是Readable,指向与它连接的那个Readable流。
  • unpipe: 当你在Readable流上调用unpipe()方法时,Writable流会发射这个事件,对应的处理器函数有一个参数,类型是Readable,指向与刚与它断开连接的那个Readable流。
  • error: 出错时发射,对应的处理器函数的参数是Error对象。
fs模块的写入流实例:

fs.createWriteStream(path[, options])
返回 WriteStream 实例对象,默认值不同,参数使用与ReadStream相似

options 常用参数
{
  flags: 'w',
  defaultEncoding: 'utf8',
  fd: null,
  mode: 0o666,
  start:0,//指定写入的开始位置
}
var fs=require('fs');
var path=require('path');
var file=path.resolve('/test1/write.txt');
var writeable=fs.createWriteStream(file,{
	highWaterMark:6,
	defaultEncoding:'utf8', //设置写入时默认的编码
	flags:'w'  //如果不存在则创建
});
//当前写入完成时触发
//调用end()方法时触发
writeable.on('finish',function(){
	console.log('写入完成');
	process.exit(0); //程序退出
});
//当前出现异常时触发
writeable.on('error',function(err){
	console.log('写入异常,'+err);
});
writeable.write('中文abcdefghijk');
//writeable.write('中文字符','ascii');  //显示指定写入编码,如果文件中有内容,同时也转码成ascii
//标记结束
writeable.end();

Duplex 流

sock.Socket是Duplex流,既实现了Readable又实现了Writable,所以,sock.pipe(sock)是正确的调用。

常见的Duplex流有:

  • TCP socket
  • zlib
  • crypto

Duplex是Readable和Writable的合体。

Transform 流

Transform扩展了Duplex流,它会修改你使用Writable接口写入的数据,当你用Readable接口来读时,数据已经发生了变化。

比较常见的Transform流有:

  • zlib
  • crypto
使用zlib模块实现压缩和解压缩实例:

压缩文件

//实例1,文件压缩
var fs=require('fs');
var zlib=require('zlib');
var path=require('path');
var file1=path.resolve('/test1/one.txt');
var file3=path.resolve('/test1/one.txt.gz');
//压缩文件
var readable1=fs.createReadStream(file1);
var writeable=fs.createWriteStream(file3);
readable1
.pipe(zlib.createGzip())
.pipe(writeable);

console.log('压缩文件完成');
解压文件
//解压文件实例
var fs=require('fs');
var zlib=require('zlib');
var path=require('path');
var file1=path.resolve('/test1/one.txt.gz');

//解压文件
var readable=fs.createReadStream(file1);
readable
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('one.gz.txt'));

readable.on('end',function(){
	console.log('解压文件完毕');
});
console.log('代码执行完毕');

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值