stream的学习实现可读流源码

// 可读流实现封装
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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值