Structure-study-Node(流)

295 阅读3分钟

可读流

可读流基本使用

    const fs = require('fs')
    let ReadStream = require('ReadStream')
    let rs = new ReadStream('./1.txt',{
        flags:'r', // 当前需要什么操作
        encoding:null, // 默认是buffer
        highWaterMark:2, // 内部会创建 64k大的buffer
        mode:438, // 操作权限 读(4) 写(2) 执行(1)
        autoClose:true, // 是否自动关闭
        start:0,
        end:4 // 包前又包后
    })
    // 默认流的模式是暂停模式
    rs.on('open',function(fd){
        console.log('文件打开触发open事件',fd)
    })
    let arr = []
    rs.on('data',function(data){
        console.log(data);
        arr.push(data);
        this.pause() // 暂停
    })

    setInterval(()=>{
        rs.resume() // 恢复读取
    },1000)

    rs.on('end',function(){
        console.log('文件读取完毕')
        console.log(Buffer.concat(arr).toString())
    })
    rs.on('close',function(){
        console.log('文件关闭')
    })

可读流代码实现

   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
           this.highWaterMark = options.highWaterMark || 64*1024
           this.mode = options.mode || 438
           this.autoClose = options.autoClose || true
           this.start = options.start || 0
           this.end = options.end

           this.flowing = null // 默认是暂停模式
           this.offset = 0; // 偏移量

           // 创建实例的时候 默认调用open
           this.open()

           this.on('newListener',(type)=>{
               // 当用户监听 data事件的时候  就开始读取文件
               if(type==='data'){
                   this.flowing = true;
                   this.read();
               }
           })
       }
       pipe(ws){
            this.on('data',(chunk)=>{
                let flag = ws.write(chunk);
                if(!flag){
                    this.pause();
                }
            });
            ws.on('drain',()=>{
                this.resume();
            })
        }
       open(){
           fs.open(this.path,this.flags,(err,fd)=>{
               this.fd = fd;
               this.emit('open',this.fd)
           })
       }
       read(){
           // 因为open是异步的 所以调用read的时候可能 没有取到fd
           if(typeof this.fd !=='number'){
               // 订阅一个事件 当open触发的时候重新调用read方法
               return this.once('open',this.read)
           }

           let howMuchToRead = this.end ? Math.min(this.highWaterMark,this.end - this.start + 1 - this.offset) : this.highWaterMark
           let buffer = Buffer.alloc(howMuchToRead)
           fs.read(this.fd,buffer,0,howMuchToRead,offset,(err,bytesRead)=>{
               this.offset += bytesRead;
               if(bytesRead > 0){
                   this.emit('data',buffer)
                   this.flowing && this.read();
               }else{
                   this.emit('end')
                   this.close()
               }
           })
       }
       close(){
           if(this.autoClose){
               fs.close(this.fd,()=>{
                   this.emit('close')
               })
           }
       }
       pause(){
           this.flowing = false
       }
       resume(){
           this.flowing = true;
           this.read()
       }
   } 

   module.exports = ReadStream

可写流

可写流基本使用

    const fs = require('fs')
    const WriteStream = require('writeStream')

    let ws = new WriteStream('./1.txt',{
        highWaterMark:3,// 表示预期占用几个内存
        encoding:'utf8',// 写入的编码
        start:0, // 从文件的第0个位置开始写入
        mode:438,
        flags:'w' // 默认操作是可写
    })

    let index = 0;
    function write(){// 可写流写入的数据只能是字符串或者buffer
        let flag = true;
        while(index<10 && flag){
            // flag 代表highWaterMark是否有空余
            flag = ws.write(index+'')
            console.log(flag)
            index++;
        }
    }
    write();

    ws.on('drain',function(){
        console.log('干了')
        write()
    })

    ws.on('close',function(){
        console.log('close')
    })

可写流代码实现

    let EventEmitter = require('events');
    let fs = require('fs');

    class WriteStream extends EventEmitter{
        constructor(path,options = {}){
            super();
            this.path = path;
            this.highWaterMark = options.highWaterMark || 16*1024
            this.encoding = options.encoding || 'utf8';
            this.start = options.start || 0;
            this.mode = options.mode || 0o666;
            this.flags = options.flags || 'w';
            // 先打开文件
            this.open(); 
            // 缓存区 
            this.cache = []; 
            this.writing = false; // 判断是否正在被写入
            this.len = 0; // 缓存区的大小
            this.needDrain = false; // 是否触发drain事件
            this.offset = this.start; // offset 表示每次写入的偏移量
        }
        open(){
            fs.open(this.fd,this.flags,(err,fd)=>{
                this.fd = fd;
                this.emit('open',fd)
            })
        }
        write(chunk,encoding=this.encoding,callback){
            // 此时这里面 用户调用write方法时 需要判断当前是否正在写入,如果正在写入 放到缓存中,如果不是正在写入 需要把他真正的像文件里写入
            chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
            this.len+=chunk.length// 统计写入数据的个数
            let flag  = this.len < this.highWaterMark;
            this.needDrain = !flag;// 正好是相反操作 只有当前写入的内容>=了highWaterMark才会触发
            if(this.writing){ // 当前是否正在写入
                // 将除了第一次真实像文件中写入的其他都放到缓存中
                this.cache.push({
                    chunk,
                    encoding,
                    callback
                })
            }else{
                this.writing = true;
                this._write(chunk,encoding,()=>{
                    callback && callback();// 先执行自己的成功操作
                    this.clearBuffer();
                })
            }
            return flag;
        }
        // 核心的写入方法
        _write(chunk,encoding,clearBuffer){
            if(typeof this.fd!=='number'){
                return this.once('open',()=>this._write(chunk,encoding,clearBuffer))
            }
            fs.write(this.fd,chunk,0,chunk.length,this.offset,(err,written)=>{
                this.offset += written;
                this.len -= written;
                clearBuffer()
            })
        }
        clearBuffer(){
            // 去缓存中取
            let obj = this.cache.shift();
            if(obj){ // 需要写入
                this._write(obj.chunk,obj.encoding,()=>{
                    obj.callback && obj.callback(); 
                    this.clearBuffer();
                });
            }else{
                if(this.needDrain){
                    this.needDrain = false; // 下一次需要重新判断是否需要触发drain事件
                    this.writing = false; //告诉下一次调用write 应该像文件中写入
                    this.emit('drain');
                }
            }
        }
        close(){
            fs.close(this.fd,()=>{
                this.emit('close')
            })
        }
        end(chunk,encoding){   // end 方法就是如果传递参数 就需要调用this._write方法
            if(chunk){
                // chunk = Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk);
                // 先讲写入的缓存 强制清空 在去写入end方法中的内容 在关闭文件
                return this.write(chunk,encoding,()=>{
                this.close()
                })
            }
            this.close();
        }
    }

    module.exports = WriteStream;

基于stream实现可读和可写流

读流

提供_read方法,调用push

    let {Readable,Writable} = require('stream')
    class MyRead extends Readable{
        _read(){
            this.push('1');
            this.push(null)
        }
    }
    let mr = new MyRead();
    mr.on('data',function(chunk){
        console.log(chunk)
    })
    mr.on('end',function(chunk){
        console.log('end')
    })

写流

提供_write方法,调用clearBuffer

    class MyWrite extends Writable{
        _write(chunk,encoding,clearBuffer){
            console.log(chunk,'----')
            clearBuffer();
        }
    }

    let myWrite = new MyWrite();
    myWrite.write('123');