Nodejs中stream操作

第一次接触Nodejs中的流(stream),是因为使用gulp做项目构建的时候。Node中有两种缓存的处理方式,一种接收全部数据,一次性从缓存读取;另一种是采用“stream”的方式,一边接受数据,一边处理。第一种方式就是传统的文件操作方式,容易理解,但遇到大文件的时候就变得不那么友好了,需要花费很长时间等待数据接收,之后再进行操作。Nodejs中的流是EventEmitter的一个实例,没收到一部分数据就触发一个事件,通过监听事件对数据进行处理,提高了效率。

Stream具有data、end、close等事件,既可以读取数据(Readable),也可以写入数据(Writable),也可以两者兼具(Duplex)和Transform。读写数据时,每读入(或写入)一段数据,就会触发一次data事件,全部读取(或写入)完毕,触发end事件。如果发生错误,则触发error事件。

一个对象只要部署了数据流接口,就可以从它读取数据,或者写入数据。Node内部很多涉及IO处理的对象,都部署了Stream接口,比如HTTP连接、文件读写、标准输入输出等。

在Nodejs中,流操作的数据格式都是字符串(string)或者二级制数(Buffer)。如果要传递JavaScript中Object,就需要开启objectMode模式。gulp中操作的就是Vinyl files的stream,因此通过Nodejs中API创建的流是无法在gulp中直接操作的,需要进行一次转化。

可读流操作

可读流有两种状刚刚新建的时候处于暂停态,当调用pipe方法、resume方法或者添加data事件的监听函数时可以激活流状态。

1.数据传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var readable = getReadableStreamSomehow();
var data;
readable.on('data', function(chunk) {
//本次得到的数据块
console.log('got %d bytes of data', chunk.length);
});
readable.on('readable', function() {
var chunk;
//没有数据返回的时候返回null
while(chunk = stream.read()) {
data += chunk;
}
});

2.数据传输结束

1
2
3
4
5
6
7
8
var readable = getReadableStreamSomehow();
readable.on('data', (chunk) => {
console.log('got %d bytes of data', chunk.length);
});
readable.on('end', () => {
console.log('there will be no more data.');
});

常用方法

1. pause()

停止触发data事件,进入暂停态。

2. resume()

继续触发data事件,进入流动太。

3. read()

从系统缓存读取并返回数据。如果读不到数据,则返回null。

4. pipe(dest[,option])

将可读流写到指定目的地,不需要做额外操作。传输完成之后,默认会调用目的地的end方法,不能再写入,可以通过参数end = false打开目的地址。

5. push(chunk[,encoding][,callback])

将数据写入可读流。chunk可以是字符串也可以是二级制数。该方法应该由流的实现者调用,在重写_read()方法中调用,调用者不应该调用push方法。

可写流操作

1.数据写入

1
2
3
4
5
6
7
8
9
var fs = require('fs');
var readableStream = fs.createReadStream('1.txt');
var writableStream = fs.createWriteStream('2.txt');
readableStream.setEncoding('utf8');
readableStream.on('data', function(chunk) {
writableStream.write(chunk);
});

常用方法

1. write(chunk[,encoding][,callback])

chunk可以是字符串也可以是二级制数。第二个参数是写入编码;第三个参数是一个回调函数,数据处理完成时,会触发这个回调函数。write方法返回一个布尔值,表示本次数据处理结果。如果返回true,数据写入成功。如果返回false,表示写入数据被缓存起来了,不能立刻写入新数据。实际仍然可以继续写入,只不过数据会被缓存到内存中。最好的方式还是等待返回true再写入。

2. end([chunk][,encoding][,callback])

chunk可以是字符串也可以是二级制数。第二个参数是写入编码;第三个参数是一个回调函数,finish事件发生时,会触发这个回调函数。

常用事件

1. drain

执行write方法返回false,当缓存数据全部写入完成,可以继续写入时,触发drain事件,表示缓存空了。

2. finish

执行end方法,所有缓存的数据释放,触发finish事件。