建立可读流html
var rs = fs.createReadStream(path,{
flags: 'r', // 打开文件要作的操做,默认为‘r’
encoding: 'utf8', // 指定解析的字符编码格式,默认为null; 同理:rs.setEncoding('utf8')
start: '3', // 开始读取的索引位置
end: '9', // 结束读取的索引位置(包括结束位置)
highWaterMark: '3', // 从底层资源读取数据并存储在内部缓冲区中的最大字节数,默认16k;
//注意:若是指定utf8编码highWaterMark要大于3个字节
});
复制代码
open事件node
// 打开要读取的文件后触发
rs.on('open', () => {
console.log('打开文件')
})
复制代码
data事件segmentfault
// 流自动从底层读取数据
rs.on('data', chunk => {
console.log(chunk);
// 调用pause()方法暂停数据的读取
rs.pause(); // 此时切换为暂停模式
})
setTimeout(() => {
// 恢复数据的读取,切换回流动模式
rs.resume();
}, 2000)
复制代码
error事件缓存
// 读取数据过程当中出现错误触发
rs.on('error', err => {
console.log(err);
})
复制代码
end事件bash
// 数据读取完毕的时候触发
rs.on('end', () => {
console.log('读取结束');
})
复制代码
close事件函数
rs.on('close', () => {
console.log('读取完毕后关闭文件');
})
复制代码
readable事件ui
rs.on('readable', () => {
// 监听readable事件切换为暂停模式,调用read()方法读取流中缓存的数据
// 当缓存中的数据不够highWaterMark,从新向底层读取highWaterMark字节的数据填充缓存区;
// 缓存区的字节数可能会大于highWaterMark
var chunk = rs.read(1)
console.log(chunk);
})
复制代码
可读流分为两种模式:this
flowing(流动模式): 当可读流监听"data"事件的时候,当前流为流动模式;可读流自动从系统底层读取数据,并经过EventEmitter发送事件来将数据提供给应用。编码
paused(暂停模式): 当可读流监听"readable"事件的时候,当前流为暂停模式;必需要调用stream.read()方法从流中读取数据片断。spa
readable._readableState.flowing 字段的值来标识当前可读流是什么模式
建立可写流
var ws = fs.createWriteStream(path, {
flags: 'w', // 打开文件要作的操做,默认是‘w’
encoding: 'utf8', // 指定写入的字符编码格式
highWaterMark: '3', // 缓存区大小(默认为16kb),
});
复制代码
write方法
* chunk 要写入的数据,类型为 buffer/string
* encoding 可选,chunk为字符串时,指定字符编码
* callback 写入完毕后的回调
var flag = ws.write(chunk, encoding, callback);
// flag 为布尔值,缓存区满时为false,不然为true
复制代码
end方法
ws.end(chunk, encoding, callback);
// 结束写入的方法,在结束的时候还能够写一部分数据进去,
// callback 若是传入,它将做为finish事件的回调函数
复制代码
drain事件
var flag = ws.write(chunk, encoding, callback);
// 当flag为false时,表示缓存区已满;当缓存区数据用完,缓存区清空的时候会触发drain事件
// 必须是在缓存区满了清空后才会触发drain事件
ws.on('drain', () => {
console.log('缓存区已清空')
})
复制代码
finish事件
ws.end('结束');
ws.on('finish', () => {
console.log('全部写入完成');
})
// 在调用了 stream.end() 方法,且缓冲区数据都已经传给底层系统以后, 'finish' 事件将被触发。
复制代码
Readable和Writable分别实现了对文件的读和写的操做;可是一般状况下会出现边读边写的场景,读取一个文件的内容,写入到另外一个文件中;在这种场景下,可能会出现读写不均衡的问题,写入比较慢,读取比较快(来不及写入的文件数据可能会丢失);因此咱们期待能够达到读写均衡的状态,因而出现了pipe(导流)。 它能够控制读取的速率,当写入较慢的时候暂停对文件的读取;当可写流缓存区数据写入完毕后恢复文件的读取。
pipe的用法
readStream.pipe(writeStream);
var from = fs.createReadStream('./1.txt');
var to = fs.createWriteStream('./2.txt');
from.pipe(to);
// pipe能够绑定多个可写流
var to2 = fs.createWriteStream('./3.txt');
from.pipe(to2);
// 分离from绑定的可写流;
// 不传参数的话会分离全部绑定的可写流
from.unpipe(to)
复制代码
pipe原理
// 当可写流调用write()方法返回false时,表示缓存区已满,这时将可读流切换为暂停模式;
// 暂停读取数据,同时监听可写流的drain事件,当缓存区数据写入完毕,触发drain事件;
// 在drain事件的回调函数中切换可读流为流动模式继续读取数据
var fs = require('fs');
var ws = fs.createWriteStream('./2.txt');
var rs = fs.createReadStream('./1.txt');
rs.on('data', data => {
var flag = ws.write(data);
if(!flag)
rs.pause();
});
ws.on('drain', () => {
rs.resume();
});
rs.on('end', () => {
ws.end();
});
复制代码
Duplex 流是同时实现了 Readable 和 Writable 接口的流 其中Readable和Writable分别是两个不相关的流
// 实现一个简单自定义的duplex须要定义好两个方法read和write
let {Duplex} = require('stream');
let index = 0;
let s = Duplex({
read(){
console.log(index)
if(index++<3){
this.push('b');
} else {
this.push(null);
}
},
write(chunk,encoding,cb){
var a = chunk.toString().toUpperCase()
console.log(1);
cb();
}
});
//process.stdin 标准输入流
//proces.stdout标准输出流
process.stdin.pipe(s)
s.pipe(process.stdout)
process.stdin.pipe(s).pipe(process.stdout);
复制代码
transform流也是一个双工流,用以处理输入输出是因果相关,位于管道中间层的 Transform 是便可读也可写的; Transform类最初继承自stream.Duplex,而且实现了它本身版本的writable._write()和readable._read()方法。 自定义一个transform流必须实现transform() 方法;
let {Transform} = require('stream');
//转换流是实现数据转换的
let t = Transform({

transform(chunk,encoding,cb){
this.push(chunk.toString().toUpperCase());
cb();
}
});
process.stdin.pipe(t).pipe(process.stdout);
复制代码
Transform 一样是双工流,看起来和 Duplex 重复了,但二者有一个重要的区别:Duplex 虽然同时具有可读流和可写流,但二者是相对独立的;Transform 的一种流的数据会通过必定的处理过程自动进入另一个流。