Node.js 中流的基础知识

node.js 是异步事件驱动,所以它很擅长处理I/O相关的任务。如果你的应用程序平台中有I/O操作,那么你可以利用Node.js中的流,流可谓是Node.js中的核心模块。让我们一起来了解和学习用流操作简单的I/O。

流是什么?

流就像是一个管道,它可以让你轻松的从源文件读取数据并且流到目的地。
就像你家楼上有一个水塔,通过管道水塔里面的水可以流到你家的厨房。
从代码的理论来说流就是EventEmitter的高级实现和一些特殊的方法。
流的实现分为3种,可读流(Readable)、可写流(Writable)、可读写流(Duplex)。可读流让你可以从一个流中读取数据,可写流让你可以往一个流中写入数据。

如果你使用过Node.js那么你可能接触过流,比如一个Node.js的HTTP服务器,request和response就是一个可读流和一个可写流。或许你也使用过fs模块,它可以创建文件的可读和可写流

现在或许你能明白流的概念了,或者你可能也不明白,但没关系下面我们来学习和了解流吧。

可读流(Readable Stream)

可读流让我们可以从一个源文件中读取数据,这个源文件可以是任何的东西。它可以是一些简单的文件,在内存中缓存的另外一个流。由于流是基于EventEmitter的,它们发出的不同点几个事件。它会在不同的时间点发出不同的事件,我们就可以利用这些事件和流一起工作

从流读取数据(Reading From Streams)

从流读取数据最好的方法是监听数据事件(data event),并附加一个回调函数,当一个数据块有效时,可读流(readable stream)会触发一个data事件,并执行你的回调函数。

来看下面代码的工作情况:

var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var data = '';
readableStream.on('data', function(chunk){
   data += chunk;
});
readableStream.on('end', function(){
   console.log(data); // hello my name is tudou
});

当调用函数 fs.createReadStream() 会创建一个可读流。创建完成后 readableStream 这个流是一个静止的状态,当你绑定了data事件,并附加了一个回调函数的时候流就开始流动(就像你打开了水龙头)。之后数据就会通过chunk流向(传递)给回调函数。流的实现者决定何时触发data事件的。举个例子,一个http请求发生了data事件,它一次可能会有几KB的可读数据。当你从文件中读取数据,你可以决定在data事件中一次一行的读取。

当没有更多的数据可读时(水流干了),流会触发end事件。在上面的代码中,当流结束时,我们监听了end事件。

还有另外一种从流读取数据的方法。你需要在流的实例上调用read(),直到数据被全部的取出。

var fs = require('fs');
var readabledStream = fs.createReadStream('file1.txt');
var data = '';
var chunk;
readabledStream.on('readable', function(){
    while( (chunk = readabledStream.read()) != null){
        data += chunk;
    }
});
readabledStream.on('end', function(){
    console.log(data); // hello my name is tudou
})

read()函数从缓冲区内部读取并返回,当没有可读的数据时返回null,所以在循环中我们检查当等于null时就终止循环。注意的是 readable 事件发生在数据可以从流中读取的情况。

设置编码(Setting Encoding)

默认情况下流读取的数据是一个Buffer对象。如果你想要读取字符串数据,那么这可能就不适合你。所以你可以调用Readable.setEncoding()设置数据编码。代码如下:

var fs = require('fs');
var readabledStream = fs.createReadStream('file1.txt');
readabledStream.setEncoding('utf8');
var data = '';
readabledStream.on('data', function(chunk){
    data += chunk;
});
readabledStream.on('end', function(){
    console.log(data); // hello my name is tudou
})

上面的例子中返回值将使用utf8编码。数据解析为utf8编码后在会调函数中就可以作为字符串使用了。

管道(Piping)

管道是一种非常屌的技术。试想一下如果你邻居家里停电并且楼上的水塔没水了怎么办,那你是不是要用盆子之类的东西给你邻居送水捏?太麻烦。为何不接上一根水管,让水自己流到你邻居家里,而不用你们去搬运水了,是不是简单捏?看下面的代码:

var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');
readableStream.pipe(writableStream);

上面的代码使用pipe()函数把file1.txt的内容写到了file2.txt,pipe()为你管理了你的数据,你不用担心数据的流动快或慢。pipe()是一个简单的数据读写工具。你只需要关注pipe()流流向的流。所以我么很容易利用多个流连接在一起。来看看吧:

链接(chaining)

假设你有一个压缩文件并且你想解压它。有很多方法可以做到这件事,但是最简单的方法是使用管道和连接。来看看下面的代码

var fs = require('fs');
var zlib = require('zlib');
fs.createReadStream('file1.txt.gz')
    .pipe(zlib.createGunzip())
    .pipe(fs.createWriteStream('output.txt'));

首先我们创建了file1.txt.gz文件的可读流,接下来我们的流流向了另外一个流zlib.createGunzip()中去解压文件,最后通过链接我们把解压后的流流向了一个可写的流。

其他的方法(Additional Method)

我们学习了一下可读的重要概念,这里关于流还有一些重要的方法也是需要学习的。
1、Readable.pause() - 这个方法会暂停流,如果流已经流动,它再也不会触发data事件。数据将会被保存到缓冲区,如果你在静态流中调用这个函数,流会开始流动,但是data事件不会被触发。
2、Readable.resume() - 恢复一个暂停的流
3、readable.unpipe() - 从管道中删除目标流,如果传递一个参数,停止特定可读流从管道中流向到目标流中。否则,所有的目标流都将被删除。

可写流(Writable Stream)

可写流让你可以写数据到目的地,想可读流一样,它也基于EventEmitter。让我们来看下各种方法和可用的事件

写流(Writing to Streams)

把一个数据写到可写流中你需要调用流的实例方法write()。看下面代码:

var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');
readableStream.setEncoding('utf8');
readableStream.on('data', function(chunk){
    writableStream.write(chunk);
});

上面的代码很简单,它从输入流中读取数据然后使用write()写入目标流中.write()函数返回一个布尔值,如果操作成功则返回true。如果返回false你将不能写任何东西。

结束数据(End of Data)

当你没有数据要写时你可以调用end()通知流,你已经完成写操作。假如是一个response对象,你经常做一下操作发送response给浏览器。

res.write(’some data!’);
res.end(‘Ended.’);

当end()被调用chunk数据将会被刷新和php的ob_end_flush()函数类型。下面的例子会返回一个错误。

res.write('some data!');
res.end();
res.write('trying to write again’);

下面是一给写流的重要事件。
1、error - 在写流时发生了一个错误。比如找不到写文件路径
2、pipe - 当一个可读流通过pipe()添加到一个可写流时发生该事件。
3、unpipe - 当一个可写流冲可读流移除时发生该事件。

总结:
流(Stream),管道(pipes)和链接(chaining)是Node.js核心和强大的功能。streams可以高性能的来操作I/O.
原文地址: http://www.sitepoint.com/basics-node-js-streams/
英语比较渣,按照我自己理解翻译的,见谅呀!😦

posted @ 2015-08-23 00:18  Monster000  阅读(235)  评论(0)    收藏  举报