20181203-join核心模块方法 + events核心模块发布订阅模式 + fs读写文件流

190 阅读4分钟

path 核心模块

  • join方法拼接路径,每次拼接都会以上次拼接的结果为基准去拼接
    path.join('./a''./b') //a/b
    path.join('./a','./b','..') //  /a
    //拼接绝对路径
    path.join(__dirname,'./a','./b'); ///Users/xiaoming/study/20181203/a/b
    
  • resolve 解析出绝对路径
    path.resolve('./a','./b') // /Users/xiaoming/study/20181203/a/b
    =>等价于path.join(__dirname,'./a','./b')
    path.resolve('/a')  // /a
    path.resolve('./a')  // /Users/xiaoming/study/20181203/a
  • 环境变量分隔符
    path.delimiter // mac: :  windows:;
  • 路径分隔符
    path.sep  //mac:/ windows:\

events 事件,内置核心模块,发布订阅模式

let EventEmitter = require('events');
let e = new EventEmitter();
/*
e.on(type,fn) //绑定事件
e.once(type,fn)//绑定事件,执行一次就删除
e.emit(type,params)//触发事件
e.removeListener(type,fn)//移除事件的指定订阅者
e.removeAllListener(type)//移除事件所有订阅者
*/
function Girl(){
    
}
let girl = new Girl();
let fn = function(param=""){
    console.log(`${param}哭`)
}
girl.on('失恋',fn)
girl.once('失恋',fn)
girl.on('失恋',(param)=>{console.log(`${param}吃`)})
girl.emit('失恋','我')
girl.removeListener('失恋',fn)
girl.emit('失恋')
girl.removeAllListener('失恋')

//es6手写发布订阅模式
class EventEmitter{
    constructor(){
        this._events = {}
    }
    on(type,cb){
        if(!this._events[type]){
            this._events[type] = [cb]
        }else{
            this._events[type].push(cb)
        }
    }
    emit(type,...args){
        this._events[type].forEach(cb=>{
            cb(...args)
        })
    }
    removeListener(type,fn){
        this._events[type] = this._events[type].filter(cb=>{
            return cb!=fn;
        })
    }
    reveAllListener(type){
        this._events[type] = [];
    }
    once(type,cb){
        let fn = (...args)=>{
            cb(...args)
            this.removeListener(type,fn)
        }
        this.on(type,fn)
    }
}

fs读写文件流

流分为可读流和可写流,流是基于事件events的。流可以实现边读边写,读一点写一点,可以控制读文件和写文件的速率,不会出现内存淹没的情况;

通过流可以实现分段读取文件,不关心文件中的内容时可以选择流。 readFile可以看到文件的具体内容

  • 可读流

读取的默认类型是buffer类型,也是内存中;默认每次读取64k

/*
    on('data',(chunk)=>{}) 监听读取事件,由非流动模式变成流动模式,只有监听了才会读出数据
    on('end',()=>{}) 默认的data事件不停的触发,每次读出的chunk,最多就是最高水位线设定的。直到文件中数据全部读完,会触发end事件。
    on('err',()=>{}) //读取出错 或者文件不存在
    rs.pause();读取暂停
    rs.resume();继续读取
*/
let fs = require('fs');
//创建可读流,可设置每次读取多少字节,默认是64*1024字节,64k;
let rs = fs.createReadStream('1.txt',{highWaterMark:1})
let arr = []
//监听读取,由非流动变为流动模式
rs.on('data',chunk=>{
    //读取的默认类型是buffer类型
    arr.push(chunk);
    //暂停读取
    rs.pause();
    //1秒后继续读取
    setTimeout(()=>{
        rs.resume();
    },1000)
})
//监听读取完毕事件
rs.on('end',()=>{
    //buffer拼接
    console.log(Buffer.concat(arr).toString())
})
rs.on('err',()=>{
    console.log('文件不存在| 读取出错')
})

  • 可写流

可写流默认每次可写入16*1024字节,16k;页可通过highWaterMark来设置最高每次写入多少;文件如果不存在,在我们创建可写流的时候,就会创建出该文件;可写流写数据,必须是字符串类型或者buffer类型,会叠加内容,不会覆盖

/*
//写文件,write是个异步操作,但是回调一般我们不会用;而且write还有返回值,表示是否已经超过最高水位线,如果是false,表示当前要写的内容超出了最高水位线,一般配合rs.pausep 和 rs.resume完成边读边写。
var flag = ws.write('1',()=>{console.log('写入完成后才会执行')});
//end方法会先把文件写进去;在关闭,调用end后,文件已经关闭就不能在write了。
ws.end('写完了,关闭可写流')

//on('drain',()=>{})写入flag变为false表示写入的东西超过最高水位线;当全部写入进去后,就会触发drain事件,表示此时可以在继续写入。
*/
let fs = require('fs')
//创建可写流
let ws = fs.createWriteStream('2.txt',{highWaterMark:3});
var flag = ws.write('1');
console.log(flag) //true
 var flag = ws.write('1');
 console.log(flag) //true
  var flag = ws.write('1');
 console.log(flag) //false //write是异步的,写完这次后,就超过了最高水位;
 
//ws.end('写完了,关闭')// 尽管write是异步,但end调用后会把所有write中的内容以最快的速度写入文件。所以监听drain事件,不能调用end

//当读入的文件全部写入后,就恢复读取,flag变为false表示写入的东西超过最高水位线;当全部写入进去后,就会触发drain事件,此时可以在继续写入。
ws.on('drain',()=>{
    console.log(‘吃完了')
})
 
  • js手动写出pipe方法,完成基于流的copy文件,边读边写。

//拷贝文件 30b文件 读取4b,读取8次 读取第一次就开始写,每次只能写1b,暂停读取,当调用drain后,在恢复读取。
    let fs = require('fs');
    fuction pipe(source,target,cb){
        let rs = fs.createReadStream(source,{highWaterMark:4})
        let ws = fs.createWriteStream(target,{highWaterMark:1});
        rs.on('data',(chunk)=>{
            let flag = ws.write(chunk);
            if(!flag){ //可写流超出最高水位线,暂停读取
                rs.pause();
            }
        })
        ws.on('drain',()=>{
            rs.resume();//当前读出的内容已经全部写入到文件中,调用继续读取。
        })
        rs.on('end',()=>{//当读取完毕,强制将内存中未写完的内容写到文件中,关闭文件。
            ws.end();
        })
    }
    pipe('1.txt','2.txt')
    
    
//可读流提供了方法,可copy到可写流中。
rs.pipe(ws);//会自动调用write 和end方法。