博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Node之文件系统fs(fileSystem)
阅读量:7155 次
发布时间:2019-06-29

本文共 19391 字,大约阅读时间需要 64 分钟。

readFile(readFileSync)(path[, options], callback)

第一个参数是文件路径、第二个参数是配置的参数例如编码格式,第三个是回调函数(同步方法是没有回调函数的)

//是把整个文件作为一个整体    fs.readFile('./1.txt',{encoding:'utf8'},function (err,data) {    	console.log(err);    	console.log(data);    });    //同步方法是没有回调函数的    let result=fs.readFileSync('./1.txt',{encoding: 'utf8'});    console.log(result);复制代码

writeFile(writeFileSync)(file, data[, options], callback)

第一个参数是文件路径、第二个参数是写入的内容,第三个是参数配置,第四个是回调函数。

fs.writeFile('./2.txt','123',{encoding: 'utf8'},(err) => {	console.log('write OK');    })    fs.writeFileSync('./3.txt','456');复制代码

简单的实现拷贝

const fs=require('fs');    function copy(src,dest,cb) {    	fs.readFile(src,(err,data) => {    		fs.writeFile(dest,data,cb);    	});    }    copy('3.txt','4.txt',() => {    	console.log('拷贝完成');    });复制代码

appendFile(追加文件内容,不能使用writeFile,因为writeFile存在的话会先清空在写入,不存在会创建)

fs.writeFile('./4.txt','789',{flag:'a'});    fs.appendFile('./4.txt','789');复制代码

flags(配置参数选项)

linux权限

编码问题(gbk->utf8转换过程中会出现一个BOM头)

let path = require('path');    let result = fs.readFileSync(path.resolve(__dirname,'./text.txt'));    console.log(stripBOM(result).toString())        function stripBOM(content){        if(Buffer.isBuffer(content)){        if(content[0] === 0xef && content[1] === 0xbb && content[2] === 0xbf ){          return content.slice(3);        }      }else{        if (content.charCodeAt(0) === 0xFEFF) {          content = content.slice(1);        }      }      return content;    }复制代码

open(filename,flags,[mode],callback)

//fd file descriptor 文件描述符 是一个数字或者说索引    fs.open('./5.txt','r',(err,fd) => {    	console.log(fd);    	fs.open('./4.txt','r',(err,fd) => {    		console.log(fd);    	});    });复制代码

read(fd, buffer, offset, length, position, callback((err, bytesRead, buffer)))

  • 第一个参数是文件描述符
  • 第二个参数用来存放读取的内容的buffer
  • 第三个参数存放内容的buffer的起始索引
  • 第四个参数存放内容的长度
  • 第五个参数读取文件的起始索引
  • 第六个参数是回调函数(bytesRead是实际读取的字节数)
fs.open('./6.txt','r',0666,(err,fd) => {	let buffer=Buffer.alloc(6);//[0,1,2,3,4,5]	fs.read(fd,buffer,0,3,3,(err,bytesRead) => {		fs.read(fd,buffer,3,3,6,(err,byteRead) => {			console.log(buffer.toString());		});	});});复制代码

write(fd, buffer[, offset[, length[, position]]], callback)

  • 第一个参数是文件描述符
  • 第二个参数用来存放要写入的内容的buffer
  • 第三个参数存放要写入的内容的buffer的起始索引
  • 第四个参数存放要写入的内容的长度
  • 第五个参数写入文件的起始索引
  • 第六个参数是回调函数
fs.open('./6.txt','r+',0666,(err,fd) => {	let buffer=Buffer.from('珠峰培训');//[0,1,2,3,4,5,6,7,8,9,10,11]	//fd buffer offset	fs.write(fd,buffer,3,6,3,(err,bytesWritten) => {		console.log(err);		// 先同步缓存在进行关闭		fs.fsync(fd,(err) => {			fs.close(fd,(err) => {				console.log('关闭文件');			});		});	});	});复制代码

fsync(fd,[callback]) (同步磁盘缓存)

close(fd,[callback])(关闭文件)

read和write实现的copy

function copy(src,dest) {	fs.open(src,'r',(err,readFd)=> {		fs.open(dest,'w',(err,writeFd) => {			let buffer=Buffer.alloc(BUFFER_SIZE);			let readed=0;			let writed=0;			function next() {				fs.read(readFd,buffer,0,BUFFER_SIZE,readed,(err,bytesRead) => {					readed+=bytesRead;					bytesRead&&fs.write(writeFd,buffer,0,bytesRead,writed,(err,bytesWritten) => {						writed+=bytesWritten;						next();					});				});			};			next();		});	});}复制代码

目录操作

  • mkdir (创建目录的时候要求父目录必须存在)
fs.mkdir('a/b/c',err => {    	console.log(err);    	console.log('创建成功');    });复制代码
  • access (判断文件是否存在)
fs.access('b',(err) => {	    console.log(err);    });复制代码

同步创建目录

function mkpSync(dir) {    	let parts=dir.split(path.sep);//['a','b','c']    	for (let i=1;i<=parts.length;i++){    		// a a/b  a/b/c    		let current=parts.slice(0,i).join(path.sep);    		try {    			fs.accessSync(current);    		} catch (err) {    			fs.mkdirSync(current);    		}    	}    }复制代码

异步创建目录

function mkpAsync(dir,callback) {	let parts=dir.split(path.sep);//[a,b,c]	let index=1;	function next() {		if (index>parts.length) return callback();		let current=parts.slice(0,index).join(path.sep);//a		index++;		fs.access(current,(err) => {			if (err) {				fs.mkdir(current,next);			} else {				next();			}		});	}	next();}复制代码

终极await/async实现创建目录

function promisify(fn) {	return function (...args) {		return new Promise((resolve,reject) => {			fn.call(null,...args,err=>err? reject():resolve());		});	}}async function mkp(dir) {	let parts=dir.split(path.sep);//['a','b','c']	for (let i=1;i<=parts.length;i++){		// a a/b  a/b/c		let current=parts.slice(0,i).join(path.sep);		try {			await promisify(fs.access)(current);		} catch (err) {			await promisify(fs.mkdir)(current);		}	}}复制代码

删除目录(要删除目录需要先把目录中的内容读出来)

读取目录内容、路径拼接、判断文件类型(不同方法处理)

  • 删除目录(fs.rmdirSync)
  • 删除文件(fs.unlinkSync)
let files = fs.readdirSync('a');    files = files.map(file => path.join('a',file));    files.forEach(file=>{      let statObj = fs.statSync(file);      if(statObj.isDirectory()){        fs.rmdirSync(file);      }else{        fs.unlinkSync(file);      }    })复制代码

先序深度优先

// 同步    let fs = require('fs');    let path = require('path');    function removeDir(p) {      let statObj = fs.statSync(p);      if(statObj.isDirectory()){        let dirs = fs.readdirSync(p);        dirs = dirs.map(dir => path.join(p,dir));        for(let i = 0; i
{ fs.stat(p,(err,statObj)=>{ // 判断文件类型 是目录 递归 否则就删除即可 if(statObj.isDirectory()){ fs.readdir(p, function (err, dirs) { // 映射路径 dirs = dirs.map(dir => path.join(p, dir)); // 映射promise dirs = dirs.map(dir => removeDir(dir)); // 删除完儿子后 删除自己 Promise.all(dirs).then(() => { fs.rmdir(p, resolve); }); }); }else{ fs.unlink(p,resolve); } }); }); } function removeDir(p,callback) { fs.stat(p,function (err,statObj) { if (statObj.isDirectory()){ fs.readdir(p,function (err,dirs) { dirs = dirs.map(dir=>path.join(p,dir)); // 我们希望 可以同时删除这些目录 if(dirs.length == 0) return fs.rmdir(p,callback); // 先预定一个函数 所有儿子都删除了的函数回调 let index = 0; function all() { index++; if (index === dirs.length) fs.rmdir(p, callback); // if (index === dirs.length) fs.rmdir(p, ()=>callback()); } dirs.forEach(dir=>{ removeDir(dir, all); }); }); }else{ fs.unlink(p,callback) } }); } // async和await let fs = require('fs'); let path = require('path'); let util = require('util'); let stat = util.promisify(fs.stat); let readdir = util.promisify(fs.readdir); let rmdir = util.promisify(fs.rmdir); let unlink = util.promisify(fs.unlink); async function removeDir(p) { let statObj = await stat(p); if(statObj.isDirectory()){ let dirs = await readdir(p); dirs = dirs.map(dir=>path.join(p,dir)); dirs = dirs.map(dir => removeDir(dir)); await Promise.all(dirs); await rmdir(p); }else{ // 要等待文件删除后 才让promise执行完 所以需要await await unlink(p); } } // 回调写法 串行 let fs = require('fs'); let path = require('path'); function removeDir(p,callback) { fs.stat(p,(err,statObj)=>{ if(statObj.isDirectory()){ fs.readdir(p,function (err,dirs) { // 异步怎么递归? // next函数用来递归的 dirs = dirs.map(dir => path.join(p, dir)); // 标识先删除第一个 function next(index) { if (index === dirs.length) return fs.rmdir(p, callback) let file = dirs[index]; // 删除目录后将下一次的删除继续传递 removeDir(file, ()=>next(index+1)); } next(0); }) }else{ // 文件删除执行callback即可 fs.unlink(p,callback); } }); }复制代码

先序广度优先

let fs = require('fs');    let path = require('path');    function removeDir(p) {      let arr = [p];      let index = 0;      let current;      while (current = arr[index++]) {        let statObj = fs.statSync(current);        if (statObj.isDirectory()) {          let dirs = fs.readdirSync(current);          arr = [...arr, ...dirs.map(dir => path.join(current, dir))];        }      }      for (let i = arr.length - 1; i >= 0; i--) {        let statObj = fs.statSync(arr[i]);        if (statObj.isDirectory()) {          fs.rmdirSync(arr[i])        }else{          fs.unlinkSync(arr[i])        }      }    }复制代码

可读流、可写流

流有两种模式 一种是暂停模式 一种是流动模式

createReadStream

let rs = fs.createReadStream('./1test.js',{      flags:'r', // 读取的方式     // encoding:null,// 编码 buffer      autoClose:true,      start:0,      end:9, // 包后      highWaterMark:2 // 最高水位线    });    let arr = []    rs.on('data',function (data) {      rs.pause(); // 暂停 暂停触发data事件      arr.push(data);      setTimeout(() => {        rs.resume();      }, 1000);    });    rs.on('error',function (err) {     console.log(err);    });    rs.on('end',function () {      console.log(Buffer.concat(arr).toString());    });复制代码

createWriteStream

写 (第一次会真的往文件里写) 后面会写到缓存中。highWaterMark只是一个标识而已,一般配合着读取来用。当写入的内容超过highWaterMark的时候会暂停一下。

let fs = require('fs');    let ws = fs.createWriteStream('2.txt',{      flags:'w',      encoding:'utf8',      autoClose:true,      start:0,      highWaterMark:3    });    let flag = ws.write('1');    console.log(flag); // true    flag = ws.write('1');    console.log(flag); // true    flag = ws.write('1');    console.log(flag); // false    ws.on('drain',function () {      console.log('抽干')    });    // 抽干方法必须当前的写入的内容(内存+文件) 已经大于等于了highWater,才会触发drain,当内容全部写入后 会执行drain方法        ws.end('我死了');//会将缓存区的内容 清空后再关闭文件    ws.write('ok');// write after end不能再结束后继续写入复制代码

手动实现的可读流(createReadStream)

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) => {          if(type === 'data'){ // 用户监听了data事件,就开始读取吧            this.flowing = true;            this.read();// 开始读取文件          }        });      }      read(){        // 这时候文件还没有打开呢,等待着文件打开后再去读取        if(typeof this.fd !== 'number'){          // 等待着文件打开,再次调用read方法          return this.once('open',()=>this.read());        }        // 开始读取了        // 文件可能有10个字符串        // start 0 end 4        // 每次读三个 3        // 0-2        // 34        let howMuchToRead = this.end ? Math.min(this.highWaterMark,this.end - this.pos+1) :this.highWaterMark        // 文件描述符 读到哪个buffer里 读取到buffer的哪个位置        // 往buffer里读取几个,读取的位置        // 想读三个 文件只有2个         fs.read(this.fd, this.buffer,0,howMuchToRead,this.pos,(err,bytesRead)=>{          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();          }        });      }      pipe(dest){        this.on('data',(data)=>{          let flag = dest.write(data);          if(!flag){            this.pause();          }        });        dest.on('drain',()=>{          this.resume();        });        this.on('end',()=>{          this.destroy();        });      }      destroy() { // 判断文件是否打开 (将文件关闭掉)        // 文件已经打开了        if (typeof this.fd === 'number') {          fs.close(this.fd, () => {            this.emit('close');          });          return;        }        this.emit('close');      }      open() { // 打开文件的逻辑        fs.open(this.path, this.flags, (err, fd) => {          if (err) {            this.emit('error', err);            if (this.autoClose) {              this.destroy(); // 销毁 关闭文件(触发close事件)            } return;          }          this.fd = fd;          this.emit('open'); // 触发文件开启事件        });      }      pause(){        this.flowing = false;      }      resume(){        this.flowing = true;        this.read(); // 继续读取      }    }    module.exports = ReadStream;复制代码

手动实现的可写流(createWriteStream)

let fs = require('fs');let EventEmitter = require('events');class WriteStream extends EventEmitter{  constructor(path,options ={}){    super();    this.path = path;    this.flags = options.flags || 'w';    this.mode = options.mode || 0o666;    this.highWaterMark = options.highWaterMark || 16*1024;    this.start = options.start || 0;    this.autoClose = options.autoClose|| true;    this.encoding = options.encoding || 'utf8';    // 是否需要触发drain事件    this.needDrain = false;    // 是否正在写入    this.writing = false;    // 缓存 正在写入就放到缓存中    this.buffer = [];    // 算一个当前缓存的个数    this.len = 0;    // 写入的时候也有位置关系    this.pos = this.start;    this.open();  }  // 0 [1 2]   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;     // this.fd    if(this.writing){      this.buffer.push({chunk,encoding,callback});    }else{      // 当文件写入后 清空缓存区的内容      this.writing = true;  // 走缓存      this._write(chunk,encoding,()=>this.clearBuffer());    }    return !this.needDrain; // write 的返回值必须是true / false  }  _write(chunk,encoding,callback){    if (typeof this.fd !== 'number') {      return this.once('open', () => this._write(chunk, encoding, callback));    }    // fd是文件描述符 chunk是数据 0 写入的位置和 长度 , this.pos偏移量    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');    }  }  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');    });  }}module.exports = WriteStream;复制代码

继承Stream的可读流

//我的流如果继承了 Readable接口 就必须要重写一个_read的方法,并且有push方法     class MyReadStream extends Readable{ // read _read        constructor(){        super();         this.index = 0;       }      _read(){        if(this.index == 5){           return this.push(null); // 读取完毕了        }         this.push(this.index+++''); // push方法也是Readble实现的       }     }     let rs = new MyReadStream();        rs.on('data',function (data) {       console.log(data);    })     rs.on('end',function () {     console.log('end');     })复制代码

继承Stream的可写流

let { Writable } = require('stream'); // 流的模块    let fs = require('fs');    // 我的流如果继承了 Readable接口 就必须要重写一个_write的方法    class MyWriteStream extends Writable { // write _write      constructor() {        super();      }      _write(chunk,encoding,clearBuffer) {        fs.appendFile('1.txt',chunk,function () {          clearBuffer();        })      }    }    let ws = new MyWriteStream();    ws.write('hello','utf8',function () {      console.log('ok');    });    ws.write('hello', 'utf8', function () {      console.log('ok');    });复制代码

readable

let fs = require('fs');    let rs = fs.createReadStream('./1.txt',{        autoClose:true,        start:0,        flags:'r',        encoding:'utf8',        highWaterMark:3// 默认先在杯子里 填 3滴水    })    // 暂停模式先把水杯 给你填满,自己去喝 喝多少取决于你自己        // 1).readable 当杯子里的水 是空的时候 会触发readable事件(还会将杯子里的水在填入 highWaterMark个)    // 2).如果当前杯子里的水 小于hightWaterMark 会再次读取highWaterMark个    // 3) 行读取器    rs.on('readable',()=>{        let r = rs.read(1);        console.log(rs._readableState.length);// 查看剩余的数量        setTimeout(()=>{            console.log(rs._readableState.length);        },5000)    });复制代码

lineReader

// 行读取器 没读完一行 就把这一行的内容 发射出来    let EventEmitter = require('events');    let fs = require('fs');    class LineReader extends EventEmitter {        constructor(path) {            super();            this.path = path;            let RETURN = 13;            let LINE = 10;            this.arr = []; // 存放内容的            // \r 13  windows 怎么表示是新的一行 就用\r            // \n 10  mac  没有\r 只有\n            this._rs = fs.createReadStream(this.path); // 64k            // 判断用户监听了newLine事件            let r ;            this.on('newListener', (type) => {                if (type === 'newLine') {                    this._rs.on('readable', () => {                        let current; // 当前读出来的内容                        while (current = this._rs.read(1)) {                            switch (current[0]) {                                case RETURN:                                    r = Buffer.concat(this.arr).toString();                                    this.emit('newLine', r);                                    this.arr = [];                                    // 如果下一个是换行 我就抛弃掉如果不是换行 我就留到数组里                                    let next = this._rs.read(1);                                    if (next[0] !== LINE) {                                        this.arr.push(current);                                    }                                    break;                                case LINE:                                    r = Buffer.concat(this.arr).toString();                                    this.emit('newLine', r);                                    this.arr = [];                                default:                                    this.arr.push(current);                            }                        }                    });                    this._rs.on('end', () => {                        let r = Buffer.concat(this.arr).toString();                        this.emit('newLine', r);                    })                }            })        }    }    // 行读取器    let lineReader = new LineReader('./1.txt');    lineReader.on('newLine', (data) => {        console.log(data, '-------------');// 123   // 456  // 789    }); 复制代码

转化流和双工流

双工流需要重写_write和_read方法。转化流重写_transform,应用场景在压缩。

let {Duplex,Transform} = require('stream');    class MyDuplex extends Duplex{      // 可能是没关系 也可能是有关系      _read(){        this.push('hello');        this.push(null);      }      _write(chunk,encoding,clearBuffer){        console.log(chunk);        clearBuffer();      }    }    let my = new MyDuplex();    my.on('data',function (data) {      console.log(data);    })    my.write('hello')    my.write('hello')        // 转化流 用的比较多的地方 压缩        class MyTransfer extends Transform{      _transform(chunk,encoding,clearBuffer){ // 参数和可写流是一样的        let str = chunk.toString().toUpperCase();        this.push(str);        clearBuffer();      }    }    let my = new MyTransfer();    // 会箭头可读流中的内容 把内容写入到可写流中    process.stdin.pipe(my).pipe(process.stdout); // 应用场景压缩复制代码

转载地址:http://aylgl.baihongyu.com/

你可能感兴趣的文章
差分约束系统
查看>>
Activity
查看>>
DBDA类
查看>>
由于某模块占用数据库连接数太大,导致其他模块无法连接数据库
查看>>
《c程序设计语言》读书笔记--首次输入不能是空符;最多10个字符
查看>>
python 协程
查看>>
JSON中使用jsonmapper解析的代码和步骤 学习笔记
查看>>
四.外键约束
查看>>
python基础之 sys.argv[]用法
查看>>
命令行打开各组件
查看>>
PowerShell_零基础自学课程_3_如何利用Powershell ISE调试PS脚本
查看>>
LeetCode-Reverse String
查看>>
解决ssh连接虚拟机卡顿
查看>>
P4284 [SHOI2014]概率充电器
查看>>
c/c++基本问题
查看>>
坦克大战
查看>>
Android Design Support Library(三)用CoordinatorLayout实现Toolbar隐藏和折叠
查看>>
Rxjava学习(一基础篇)
查看>>
自定义 密码输入框+数字键盘
查看>>
启动hadoop,报错Error JAVA_HOME is not set and could not be found
查看>>