// 可读流实现封装
const EventEmitter = require('events')
const fs = require('fs')
class ReadStream extends EventEmitter{
constructor(path,options={}){
// 继承父类的属性
super()
// 读取的路径
this.path = path;
// 申请开启的类型权限
this.flags = options.flags || 'r';
// 默认编码格式
this.encoding = options.encoding || null ; // 默认变化是buffer类型
// 设置是否读取完成自动结束
if(typeof options.autoClose == 'undefined'){
this.autoClose = true;
}else{
this.autoClose = options.autoClose;
}
// 设置读取的开始位置
this.start = options.start || 0;
// 读取的结束位置
this.end = options.end || undefined;
// 默认获取是流类型的 可以使用暂停
this.flowing = true;
// 最大读取数量 默认是64kb
this.highWaterMark = options.highWaterMark || 64 *1024;
this.open(); // 默认就会调用开启事件
this.offset = this.start; // offset可以根据每次读取的实际个数来变化
// 订阅 newListener方法 判断是否用户监听了data事件 如果监听了data事件那么调用实例的read方法
this.on('newListener',(type)=>{
if(type == 'data'){
this.read();
}
})
}
// 暂停读取事件
pause(){
this.flowing = false;
}
// 恢复读取事件
resume(){
// 恢复流式 并且再次读取 但是为了避免连读调用恢复读取 需要判断如果恢复了那么就不需要恢复了
if(!this.flowing){
this.flowing = true;
this.read()
}
}
// 调用开启事件
open(){
fs.open(this.path,this.flags,(err,fd)=>{
// 开启失败 发布error事件 触发这个事件
if(err){
return this.emit('error',err)
}
this.fd = fd; // 将文件标识符挂载到当前的实例对象上面
console.log('fd',fd)
this.emit('open',fd); // 发布open事件
})
}
read(){
// 在read里面我们可能读取不到fd 方法 根据事件循环机制 this.on方法都是宏任务的同步方法,
// 而open中的fs.open方法是微任务的异步方法,则会等待所有的同步方法执行完成才会执行open方法所以在read中没办法得到fd这个属性
// 所以我们想到一个办法 要知道这个open方法完成那就在这里监听一次呗 监听open方法成功然后在调用read方法
if(typeof this.fd != 'number'){
this.once('open',()=>this.read())
return;
}
// 这里一定能获取到fd 获取到fd以后开始使用fs的read方法
// 由于buffer是引用类型所以每次分段读取存入到的buffer对象(是内存)不可以使用同一个对象,因为你返回过去的话都是一个对象,值改变了但是地址没改变 数组中的都是一个对象 那么值就是一个了 所以每次读取的时候都要创建新的buffer
// 声明一个内存为highWaterMark大的对象
const buffer = Buffer.alloc(this.highWaterMark);
// 这里要判断 实际写入的大小 因为 start 0 end 4 highWaterMark 2 总共是 end + 1 -start 个数
// 每次读取 2个 每次读取 offset为2 每次剩下 end + 1 - offset个 所以要判断剩下的和每次读取最大的哪个最小,最小的就是实际读取的值和写入的值 所以这块要进行一个比较
let howMuchToRead = Math.min((this.end + 1 - this.offset),this.highWaterMark)
console.log(howMuchToRead)
fs.read(this.fd,buffer,0,howMuchToRead,this.offset,(err,bytesRead)=>{
// 读取的个数
if(bytesRead){
this.offset += bytesRead;
// 有可能当前剩余的没有highWaterMark大所以要截取实际的值
this.emit('data',buffer.slice(0,bytesRead))
if(this.flowing){
// 读取完成以后再继续读取 并且还是流类型
this.read()
}
}else{
// 如果没有了 出发结束事件
this.emit('end');
// 判断如果自动关闭了那么就关闭连接
if(this.autoClose){
fs.close(this.fd,()=>{
this.emit('close')
})
}
}
})
}
}
module.exports = ReadStream;
stream的学习实现可读流源码
最新推荐文章于 2024-03-15 01:39:30 发布