Stream
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。 Node.js,Stream 有四种流类型:(流有两种模式:暂停模式、流动模式)
-详细的api介绍请查看nodejs.cn/api/stream.…
- Readable -- 可读操作。
可写流(createWriteStream)
- on('drain')
- ws.write(chuck, encoding, callbck) 返回Boolen值 第一次真的写入,之后写入放放缓存区
let fs = require('fs');
let ws = fs.createWriteStream('./test2.txt', {
flags: 'w', //读取的方式
encoding: 'utf8', //编码
autoClose: true,
start: 0,
highWaterMark: 3 //最高水位线
});
//第一次会入到文件内,后面写到缓存中
//write方法. chuck --> string or buffer;
let flag = ws.write('1');
console.log(flag);
flag = ws.write('1');
console.log(flag);
flag = ws.write('1');
console.log(flag);
//highWaterMark 是个标识,配合读取用
//highWaterMark 使用内存大小 默认64k, 读取后超出最高水位线应该暂停
//写完调用drain方法
ws.on('drain', () => {
console.log('抽干')
})
//drain方法,必须当前的写入内容已经大于highWater才会触发drain
//当内容全部写入后,执行drain方法
ws.end('关闭了');//会将缓存内容清空后.再关闭文件
ws.write('Ok') //不能再结束后,继续写入
复制代码
实现可写流
let fs = require('fs');
let EventEmitter = require('events');
class WriteStream extends EventEmitter {
constructor (path, options = {}) {
super();
this.path = path;
this.autoClose = options.autoClose || true;
this.encoding = options.encoding || null;
this.highWaterMark = options.highWaterMark || 16 * 1024;
this.mode = options.mode || 0o666;
this.start = options.start || 0;
this.flags = options.flags || 'w';
//是否需要触发drain事件
this.needDrain = false;
//是否正在写入
this.writing = false;
//正在写入放在缓存中
this.buffer = [];
//缓存个数
this.len = 0;
//写入时候的位置关系
this.pos = this.start;
this.open();
}
destroy(){
if(typeof this.fd === 'number'){
fs.close(this.fd, () =>{
this.emit('close');
});
return
}
this.emit('close');
}
open() {
fs.open(this.path, this.flags, this.mode, (err, fd)=> {
if(err){
this.emit('error');
this.destroy();
return
}
this.fd = fd;
this.emit('open');
});
}
write(chunk, encoding = this.encoding, callback) {
chunk = Buffer.isBuffer(chunk) ? chunk:Buffer.from(chunk);
this.len += chunk.length;// 每次调用write就统计一下长度
this.needDrain = this.highWaterMark <= this.len;
if (this.writing) {
this.buffer.push({chunk, encoding, callback})
}else {
//当文件写入后,清空缓存区的内容
this.writing = true; //走缓存
this._write(chunk, encoding, () => this.clearBuffer());
};
return !this.needDrain;
}
_write(chunk, encoding, callback) {
if (typeof this.fd !== 'number') {
return this.once('open', () => this._write(
chunk, encoding, callback
))
}
/*
@params: fd --> Number --> 文件描述符
@params: chuck -->数据
@parmas: offset --> 写入位置
@params: length --> 写入长度
@params: position --> 偏移量
*/
fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, bytesWritten) => {
this.pos += bytesWritten;
this.len -= bytesWritten; //写扩的长度会减少
callback();
})
}
clearBuffer() {
let buf = this.buffer.shift();
if(buf){
this._write(buf.chunk, buf.encoding, () => this.clearBuffer());
}else{
this.writing = false;
this.needDrain = false; // 触发一次drain 再置回false 方便下次继续判断
this.emit('drain');
}
}
}
module.exports = WriteStream;
复制代码
验证可写流实现
let fs = require('fs');
let WriteStream = require('./WriteStream');
let ws = new WriteStream('./3.txt', {
flags: 'w',
highWaterMark: 3,
autoClose: true,
encoding: 'utf8',
start: 0
});
let i = 0;
function write() {
let flag = true;
while ( i < 9 && flag) {
flag = ws.write(i++ + '');
}
}
ws.on('drain', () =>{
console.log('写入成功')
write();
})
write();
输出结果如下
写入成功
写入成功
写入成功
复制代码
可读流(createReadStream)
- on('open') 打开
- on('data') 流动模式
- on('end') 结束
- on('close') 关闭
- rs.pause 暂停
- rs.resume 恢复
//读文件(文件需存在)
let fs = require('fs');
let rs = fs.createReadStream('./tes1t.txt', {
flags: 'r', //读取的方式
encoding: null, //编码 buffer
autoClose: true,
start: 0,
end: 9, // 包后
highWaterMark: 2 //最高水位线
})
//默认情况下,不会读取文件内
let arr = [];
rs.on('data', (data) => {
rs.pause(); //暂停 暂停触发data事件
arr.push(data);
console.log("停")
setTimeout(() => {
rs.resume(); //恢复
})
})
rs.on('error', (err) => {
console.log("err", err)
})
rs.on('end', function(){
console.log(Buffer.concat(arr).toString())
})
复制代码
实现可读流
let EventEmitter = require('events');
let fs = require('fs');
class ReadStream extends EventEmitter {
constructor(path, options = {}) {
super();
this.path = path;
this.autoClose = options.autoClose || true;
this.flags = options.flags || 'r';
this.encoding = options.encoding || null;
this.start = options.start || 0;
this.end = options.end || null;
this.highWaterMark = options.highWaterMark || 64 * 1024;
//读取文件的位置(可变的)
this.pos = this.start;
//控制当前是否是流动模式
this.flowing = null;
//构建读取内容buffer
this.buffer = Buffer.alloc(this.highWaterMark);
//当创建可读流,要将文件打开 --> 异步执行
this.open();
this.on('newListener', (type) => {
//用户监听data事件
if (type === 'data') {
//改成流动模式
this.flowing = true;
this.read();
}
})
}
read() {
//文件还没有打开,等待文件后再去读取
if (typeof this.fd !== 'number') {
//等待文件打开,再次调用援藏
return this.once('open', () => this.read());
}
//开始读取
/*
@params: fd --> Number --> 文件描述符
@params: buffer --> 读到buffer里
@parmas: offset --> 读取到buffer的位置
@params: length --> 读取几个到buffer里
@params: position --> 读取文件的位置
*/
let howMuchToRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark
fs.read(this.fd, this.buffer, 0, howMuchToRead, this.pos, (err, bytesRead) => {
if (err) {
this.emit('error');
this.destroy();
return;
}
if (bytesRead > 0) {
this.pos += bytesRead;
//保留有效的
let r = this.buffer.slice(0, bytesRead);
r = this.encoding ? r.toString(this.encoding) : r;
//第一次读取
this.emit('data', r);
if (this.flowing) {
this.read();
}
}else {
this.emit('end');
this.destroy();
}
});
}
destroy() {
//判断文件是否打开(交文件关闭)
if (typeof this.fd === 'number') {
fs.close(this.fd, () => {
this.emit('close');
});
return;
}
this.close('close');
}
open() {
fs.open(this.path, this.flags, (err, fd) => {
if (err) {
this.emit('error', err);
if (this.autoClose) {
// 销毁,关闭文件(触发close事件)
this.destroy();
}
return;
}
this.fd = fd;
//触发文件开户事件
this.emit('open');
})
}
pause() {
this.flowing = false;
}
resume() {
this.flowing = true;
this.read();
}
}
module.exports = ReadStream;
复制代码
验证可读流实现
let fs = require('fs');
let ReadStream = require('./ReadStream.js');
let rs = new ReadStream('./test.txt', {
flags: 'r', //读取的方式
encoding: null, //编码buffer,
autoClose: true,
start: 0,
end: 4, // 包后
highWaterMark: 2, //最高水位线,
encoding: 'utf8'
})
let arr = [];
rs.on('open', () => {
console.log('open');
})
rs.on('data', (data) => {
console.log(data);
rs.pause();
setTimeout(() => {
rs.resume()
}, 1000)
});
rs.on('error', (err) => {
console.log("err", err)
})
rs.on('end', function(){
console.log("完毕");
})
rs.on('close', function(){
console.log("close");
});
输出结果如下:
open
12
34
5
完毕
close
复制代码