1. fs模块 #

2. 整体读取文件 #

2.1 异步读取 #

fs.readFile(path[, options], callback)

2.2 同步读取 #

fs.readFileSync(path[, options])

3. 写入文件 #

3.1 异步写入 #

fs.writeFile(file, data[, options], callback)
let fs = require('fs');
fs.writeFile('./1.txt',Date.now()+'\n',{flag:'a'},function(){
  console.log('ok');
});

3.2 同步写入 #

fs.writeFileSync(file, data[, options])

3.3 追加文件 #

fs.appendFile(file, data[, options], callback)

fs.appendFile('./1.txt',Date.now()+'\n',function(){
  console.log('ok');
})

3.4 拷贝文件 #

function copy(src,target){
  fs.readFile(src,function(err,data){
    fs.writeFile(target,data);
  })
}

4. 从指定位置处开始读取文件 #

4.1 打开文件 #

fs.open(filename,flags,[mode],callback);

  • FileDescriptor 是文件描述符
  • FileDescriptor 可以被用来表示文件
  • in -- 标准输入(键盘)的描述符
  • out -- 标准输出(屏幕)的描述符
  • err -- 标准错误输出(屏幕)的描述符
fs.open('./1,txt','r',0600,function(err,fd){});

4.2 读取文件 #

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

//珠峰培训
const fs=require('fs');
const path=require('path');
fs.open(path.join(__dirname,'1.txt'),'r',0o666,function (err,fd) {
    console.log(err);
    let buf = Buffer.alloc(6);
     fs.read(fd,buf,0,6,3,function(err, bytesRead, buffer){
       console.log(bytesRead);//6
       console.log(buffer===buf);//true
       console.log(buf.toString());//峰培
     })
})

4.3 写入文件 #

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

const fs=require('fs');
const path=require('path');
fs.open(path.join(__dirname,'1.txt'),'w',0o666,function (err,fd) {
    console.log(err);
    let buf=Buffer.from('珠峰培训');
     fs.write(fd,buf,3,6,0,function(err, bytesWritten, buffer){
       console.log(bytesWritten);//6
       console.log(buffer===buf);//true
       console.log(buf.toString());//珠峰培训
     })
})

4.4 同步磁盘缓存 #

fs.fsync(fd,[callback]);

4.5 关闭文件 #

fs.close(fd,[callback]);

let buf = Buffer.from('珠峰培训');
fs.open('./2.txt', 'w', function (err, fd) {
  fs.write(fd, buf, 3, 6, 0, function (err, written, buffer) {
    console.log(written);
    fs.fsync(fd, function (err) {
      fs.close(fd, function (err) {
          console.log('写入完毕!')
        }
      );
    });
  })
});

4.6 拷贝文件 #

let BUFFER_SIZE=1;
const path=require('path');
const fs=require('fs');
function copy(src,dest,callback) {
    let buf=Buffer.alloc(BUFFER_SIZE);
    fs.open(src,'r',(err,readFd)=>{
        fs.open(dest,'w',(err,writeFd) => {
            !function read() {
                fs.read(readFd,buf,0,BUFFER_SIZE,null,(err,bytesRead) => {
                    bytesRead&&fs.write(writeFd,buf,0,bytesRead,read);
                });
            }()
        })
    });
}
copy(path.join(__dirname,'1.txt'),path.join(__dirname,'2.txt'),()=>console.log('ok'));

5 目录操作 #

5.1 创建目录 #

fs.mkdir(path[, mode], callback)

要求父目录必须存在

5.2 判断一个文件是否有权限访问 #

fs.access(path[, mode], callback)

fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (err) => {
  console.log(err ? 'no access!' : 'can read/write');
});

5.3 读取目录下所有的文件 #

fs.readdir(path[, options], callback)

5.4 查看文件目录信息 #

fs.stat(path, callback)

5.5 移动文件或目录 #

fs.rename(oldPath, newPath, callback)

5.6 删除文件 #

fs.unlink(path, callback)

5.7 截断文件 #

fs.ftruncate(fd[, len], callback)
const fd = fs.openSync('temp.txt', 'r+');
// 截断文件至前4个字节
fs.ftruncate(fd, 4, (err) => {
  console.log(fs.readFileSync('temp.txt', 'utf8'));
});

5.8 监视文件或目录 #

fs.watchFile(filename[, options], listener)
let fs = require('fs');
fs.watchFile('1.txt', (curr, prev) => {
  //parse() 方法可解析一个日期时间字符串,并返回 1970/1/1 午夜距离该日期时间的毫秒数。
  if(Date.parse(prev.ctime)==0){
    console.log('创建');
  }else if(Date.parse(curr.ctime)==0){
    console.log('删除');
  }else if(Date.parse(prev.ctime) != Date.parse(curr.ctime)){
    console.log('修改');
  }
});

6 递归创建目录 #

6.1 同步创建目录 #

let fs=require('fs');
let path=require('path');
function makepSync(dir) {
    let parts=dir.split(path.sep);
    for (let i=1;i<=parts.length;i++){
        let parent=parts.slice(0,i).join(path.sep);
        try {
            fs.accessSync(parent);
        } catch (error) {
            fs.mkdirSync(parent);
        }

    }
}

6.2 异步创建目录 #

function makepAsync(dir,callback) {
    let parts=dir.split(path.sep);
    let i=1;
    function next() {
        if (i>parts.length)
            return callback&&callback();    
        let parent=parts.slice(0,i++).join(path.sep);
        fs.access(parent,err => {
            if (err) {
                fs.mkdir(parent,next);
            } else {
                next();
            }
        });
    }
    next();
}

6.3 Async+Await创建目录 #

async function mkdir(parent) {
    return new Promise((resolve,reject) => {
        fs.mkdir(parent,err => {
            if (err) reject(err);
            else resolve();
        });
    });
}

async function access(parent) {
    return new Promise((resolve,reject) => {
        fs.access(parent,err => {
            if (err) reject(err);
            else resolve();
        });
    });
}
async function makepPromise(dir,callback) {
    let parts=dir.split(path.sep);
    for (let i=1;i<=parts.length;i++){
        let parent=parts.slice(0,i).join(path.sep);
        try {
            await access(parent);
        }catch(err) {
            await mkdir(parent);
        }

    }
}

7. 递归删除目录 #

7.1 同步删除目录(深度优先) #

let fs=require('fs');
let path=require('path')
function rmSync(dir) {
    try {
        let stat = fs.statSync(dir);
        if (stat.isFile()) {
            fs.unlinkSync(dir);
        } else {
            let files=fs.readdirSync(dir);
            files
                .map(file => path.join(dir,file))
                .forEach(item=>rmSync(item));
            fs.rmdirSync(dir);
        }
    } catch (e) {
        console.log('删除失败!');
    }
}
rmSync(path.join(__dirname,'a'));

7.2 异步删除非空目录(Promise版) #

function rmPromise(dir) {
    return new Promise((resolve,reject) => {
        fs.stat(dir,(err,stat) => {
            if (err) return reject(err);
            if (stat.isDirectory()) {
                fs.readdir(dir,(err,files) => {
                    let paths = files.map(file => path.join(dir,file));
                    let promises = paths.map(p=>rmPromise(p));
                    Promise.all(promises).then((() => fs.rmdir(dir,resolve)));
                });
            } else {
                fs.unlink(dir,resolve);
            }
        });
    });
}
rmPromise(path.join(__dirname,'a')).then(() => {
    console.log('删除成功');
})

7.3 异步串行删除目录(深度优先) #

function rmAsyncSeries(dir,callback) {
    setTimeout(() => {
        fs.stat(dir,(err,stat) => {
            if (err) return callback(err);
            if (stat.isDirectory()) {
                fs.readdir(dir,(err,files) => {
                    let paths = files.map(file => path.join(dir,file));
                    function next(index) {
                        if (index>=files.length) return fs.rmdir(dir,callback);
                        let current=paths[index];
                        rmAsyncSeries(current,()=>next(index+1));
                    }
                    next(0);
                });
            } else {
                fs.unlink(dir,callback);
            }
        })
    },1000);
}

console.time('cost');
rmAsyncSeries(path.join(__dirname,'a'),err => {
     console.timeEnd('cost');
})

7.4 异步并行删除目录(深度优先) #

function rmAsyncParallel(dir,callback) {
    setTimeout(() => {
        fs.stat(dir,(err,stat) => {
            if (err) return callback(err);
            if (stat.isDirectory()) {
                fs.readdir(dir,(err,files) => {
                    let paths=files.map(file => path.join(dir,file));
                    if (paths.length>0) {
                        let i=0;
                        function done() {
                            if (++i == paths.length) {
                                fs.rmdir(dir,callback);
                            }
                        }
                      paths.forEach(p=>rmAsyncParallel(p,done));
                    } else {
                        fs.rmdir(dir,callback);
                    }
                });
            } else {
                fs.unlink(dir,callback);
            }
        })
    },1000);
}
console.time('cost');
rmAsyncParallel(path.join(__dirname,'a'),err => {
     console.timeEnd('cost');
})

7.5 同步删除目录(广度优先) #

function rmSync(dir){
    let arr=[dir];
    let index=0;
    while (arr[index]) {
        let current=arr[index++];
        let stat=fs.statSync(current);
        if (stat.isDirectory()) {
            let dirs=fs.readdirSync(current);
            arr=[...arr,...dirs.map(d => path.join(current,d))];
        }
    }
    let item;
    while (null != (item = arr.pop())) {
        let stat = fs.statSync(item);
        if (stat.isDirectory()) {
            fs.rmdirSync(item);
        } else {
            fs.unlinkSync(item);
        }
    }
}

7.6 异步删除目录(广度优先) #

function rmdirWideAsync(dir,callback){
    let dirs=[dir];
    let index=0;
    function rmdir() {
        let current = dirs.pop();
        if (current) {
            fs.stat(current,(err,stat) => {
                if (stat.isDirectory()) {
                    fs.rmdir(current,rmdir);
                } else {
                    fs.unlink(current,rmdir);
                }
            });
        }
    }
    !function next() {
        let current=dirs[index++];
        if (current) {
            fs.stat(current,(err,stat) => {
                if (err) callback(err);
                if (stat.isDirectory()) {
                    fs.readdir(current,(err,files) => {
                        dirs=[...dirs,...files.map(item => path.join(current,item))];
                        next();
                    });
                } else {
                    next();
                }
            });
        } else {
            rmdir();
        }

    }();
}

8. 遍历算法 #

8.4 异步广度优先+先序遍历 #

// 异步广度遍历
function wide(dir, cb) {
    console.log(dir);
    cb && cb()
    fs.readdir(dir, (err, files) => {
        !function next(i){
            if(i>= files.length) return;
            let child = path.join(dir,files[i]);
            fs.stat(child,(err,stat)=>{
                if(stat.isDirectory()){
                    wide(child, () => next(i+1));
                } else {
                    console.log(child);
                    next(i+1);
                }
            })
        }(0);
    })
}
wide(path.join(__dirname,'a'));

8. path模块 #

path是node中专门处理路径的一个核心模块

var path = require('path');
var fs = require('fs');
/**
 * normalize 将非标准化的路径转化成标准化的路径
 * 1.解析. 和 ..
 * 2.多个斜杠会转成一个斜杠
 * 3.window下的斜杠会转成正斜杠
 * 4.如果以斜杠会保留
 **/

console.log(path.normalize('./a////b//..\\c//e//..//'));
//  \a\c\

//多个参数字符串合并成一个路径 字符串
console.log(path.join(__dirname,'a','b'));

/**
 * resolve
 * 以就用程序为根目录,做为起点,根据参数解析出一个绝对路径
 *  1.以应用程序为根起点
 *  2... .
 *  3. 普通 字符串代表子目录
 *  4. /代表绝地路径根目录
 */
console.log(path.resolve());//空代表当前的目录 路径
console.log(path.resolve('a','/c'));// /a/b
// d:\c
//可以获取两个路径之间的相对关系
console.log(path.relative(__dirname,'/a'));
// a
//返回指定路径的所在目录
console.log(path.dirname(__filename)); // 9.path
console.log(path.dirname('./1.path.js'));//  9.path
//basename 获取路径中的文件名
console.log(path.basename(__filename));
console.log(path.basename(__filename,'.js'));
console.log(path.extname(__filename));

console.log(path.sep);//文件分隔符 window \ linux /
console.log(path.win32.sep);
console.log(path.posix.sep);
console.log(path.delimiter);//路径 分隔符 window ; linux :

9. flags #

符号 含义
r 读文件,文件不存在报错
r+ 读取并写入,文件不存在报错
rs 同步读取文件并忽略缓存
w 写入文件,不存在则创建,存在则清空
wx 排它写入文件
w+ 读取并写入文件,不存在则创建,存在则清空
wx+ 和w+类似,排他方式打开
a 追加写入
ax 与a类似,排他方式写入
a+ 读取并追加写入,不存在则创建
ax+ 作用与a+类似,但是以排他方式打开文件

10. 助记 #

11. linux权限 #

文件类型与权限 链接占用的节点(i-node) 文件所有者 文件所有者的用户组 文件大小 文件的创建时间 最近修改时间 文件名称

权限