每天学点node系列-fs文件系统(续)

时间:2021-11-29 09:05:31

理想如果不向现实做一点点屈服,那么理想也将归于尘土。

创建目录

异步创建目录

path<string> | <Buffer> | <URL>
options <Object> | <integer>
  - recursive <boolean> 默认值: false。
  - mode <integer> Windows 上不支持。默认值: 0o777。
callback <Function>
  - err <Error></Error>
// fs.mkdir(path[, mode], callback)
var fs = require('fs')
fs.mkdir('sub', function(err){
    if(err) throw err
    console.log('创建目录成功')
})

执行结果:

$ node 1.js
创建目录成功

同步创建目录

path<string> | <Buffer> | <URL>
  - options <Object> | <integer>
  - recursive <boolean> 默认值: false。
mode <integer> Windows 上不支持。默认值: 0o777。
// fs.mkdirSync(path[, mode])
var fs = require('fs')
try{
    fs.mkdirSync('rock')
    console.log('创建目录成功')
}catch(e){
    throw e
}

执行结果:

$ node 1.js
创建目录成功

读取目录

异步读取目录

path<string> | <Buffer> | <URL>
options <string> | <Object>
  - encoding <string> 默认值: 'utf8'。
  - withFileTypes <boolean> 默认值: false。
callback <Function>
  - err <Error>
  - files <string[]> | <Buffer[]> | <fs.Dirent[]></Error>

注意: files 是目录中的文件名的数组(不包括 '.' 和 '..')

let fs = require('fs')
fs.readdir('./sub', 'utf8', (err, files) => {
    if (err) console.log(err)
    console.log(files)
})

执行结果:

$ node 1.js
[ '1.css', 'index.html' ]

同步读取目录

path <string> | <Buffer> | <URL>
options <string> | <Object>
  - encoding <string> 默认值: 'utf8'。设置为 'buffer',则返回的文件名是 Buffer 对象。
  - withFileTypes <boolean> 默认值: false。 设置为 true,则返回的结果将包含 fs.Dirent 对象。
返回: <string[]> | <Buffer[]> | <fs.Dirent[]></boolean>
let fs = require('fs')
try {
    let result = fs.readdirSync('./sub', 'utf8')
    console.log(result)
} catch (error) {
    console.log(error)
}

执行结果:

$ node 1.js
[ '1.css', 'index.html' ]

遍历目录

var fs = require('fs')
var path = require('path')
var getFilesInDir = function (dir) {
    var results = [path.resolve(dir)]
    var files = fs.readdirSync(dir, 'utf8')
    files.forEach(function (file) {
        file = path.resolve(dir, file)
        var stats = fs.statSync(file)
        if (stats.isFile()) {
            results.push(file)
        } else if (stats.isDirectory()) {
            results = results.concat(getFilesInDir(file))
        }
    })
    return results
}
var files = getFilesInDir('./api');
console.log(files);

注意:fs.readdirSync()只会读一层,所以需要判断文件类型是否目录,如果是,则进行递归遍历。

执行结果:

$ node 1.js
[ 'C:\\Users\\Administrator\\Desktop\\api',
  'C:\\Users\\Administrator\\Desktop\\api\\config.1.json',
  'C:\\Users\\Administrator\\Desktop\\api\\config.json',
  'C:\\Users\\Administrator\\Desktop\\api\\config16.json',
  'C:\\Users\\Administrator\\Desktop\\api\\integral-consumpte'
...

修改权限

path <string> | <Buffer> | <URL>
mode <integer>
callback <Function>
  - err <Error>
var fs = require('fs')
fs.chmod('1.txt', '777', function (err) {
    if (err) {
        console.log(err)
    } else {
        console.log("change done")
    }
})

同步修改所有者

path <string> | <Buffer> | <URL>
mode <integer>
var fs = require('fs')
fs.chmodSync('./1.txt', '777')

访问/权限检测

path<string> | <Buffer> | <URL>
    mode <integer> 默认值: fs.constants.F_OK。
    callback <Function>
  - err <Error></Error>

const fs = require('fs')
const file = '1.txt';
// 检查当前目录中是否存在该文件。
fs.access(file, fs.constants.F_OK, (err) => {
    console.log(`${file} ${err ? '不存在' : '存在'}`);
});
// 检查文件是否可读。
fs.access(file, fs.constants.R_OK, (err) => {
    console.log(`${file} ${err ? '不可读' : '可读'}`);
});
// 检查文件是否可写。
fs.access(file, fs.constants.W_OK, (err) => {
    console.log(`${file} ${err ? '不可写' : '可写'}`);
});
// 检查当前目录中是否存在该文件,以及该文件是否可写。
fs.access(file, fs.constants.F_OK | fs.constants.W_OK, (err) => {
if (err) {
    console.error(
        `${file} ${err.code === 'ENOENT' ? '不存在' : '只可读'}`);
    } else {
        console.log(`${file} 存在,且它是可写的`);
    }
});

执行结果:

$ node 1.js
1.txt 存在
1.txt 可读
1.txt 可写
1.txt 存在,且它是可写的

不建议在调用 fs.open()、 fs.readFile() 或 fs.writeFile() 之前使用 fs.access() 检查文件的可访问性。 这样做会引入竞态条件,因为其他进程可能会在两个调用之间更改文件的状态。 相反,应该直接打开、读取或写入文件,如果文件无法访问则处理引发的错误。

写入(不推荐)


const fs = require('fs')
const file = '1.txt'
fs.access(file, (err) => {
    if (!err) {
        console.error(`${file} 已存在`)
        return
    }
    fs.open(file, 'wx', (err, fd) => {
        if (err) throw err
        writeMyData(fd)
    })
})

写入(推荐)

const fs = require('fs')
fs.open('1.txt', 'wx', (err, fd) => {
    if (err) {
        if (err.code === 'EEXIST') {
            console.error('1.txt 已存在')
            return
        }
        throw err
    }
    writeMyData(fd)
})

同步访问/权限检测

path<string> | <Buffer> | <URL>
mode <integer> 默认值: fs.constants.F_OK。

注意: 如果可访问性检查失败,则抛出 Error。 否则,该方法将返回 undefined。

try {
    fs.accessSync('./1.txt', fs.constants.R_OK | fs.constants.W_OK)
    console.log('可以读写')
} catch (err) {
    console.error('无权访问')
}

删除目录

异步删除目录

path<string> | <Buffer> | <URL>
callback <Function>
  - err <Error>

注意:在文件(而不是目录)上使用 fs.rmdir() 会导致在 Windows 上出现 ENOENT 错误、在 POSIX 上出现 ENOTDIR 错误。

var fs = require('fs')

fs.rmdir('./rock', function(err){
    if(err) throw err
    console.log('目录删除成功')
})

同步删除目录

path <string> | <Buffer> | <URL>
var fs = require('fs')
fs.rmdirSync('./rock')

实用场景

文件锁

使用独占标记创建锁文件

fs模块为所有需要打开文件的方法提供了一个x标记。这告诉操作系统这个文件应该以独占模式打开(O_EXCL)。当使用这个方法时,若这个文件存在,文件不能被打开。

文件数据库

策略是不断的追加记录,在处理的时候不断的覆盖原始记录。

var fs = require('fs')
var EventEmitter = require('events').EventEmitter
var Database = function (path) {
    this.path = path
    // 数据库key/value映射表
    this._records = Object.create(null)
    this._writeStream = fs.createWriteStream(this.path, {
        encoding: 'utf8',
        flags: 'a'
    })
    this._load()
}
// 继承自EventEmitter,让它有可以监听事件、传递事件的功能
Database.prototype = Object.create(EventEmitter.prototype)
Database.prototype._load = function () {
    var stream = fs.createReadStream(this.path, { encoding: 'utf8' })
    var database = this
    var data = ''
    // 当输入流准备好时触发
    stream.on('readable', function () {
        data += stream.read()
        // 每一行是一组数据
        var records = data.split('\n')
        data = records.pop()
        for (var i = 0; i < records.length; i++) {
            try {
                var record = JSON.parse(records[i])
                if (record.value == null) {
                    delete database._records[record.key]
                } else {
                    database._records[record.key] = record.value
                }
            } catch (e) {
                database.emit('error', 'found invalid record:', records[i])
            }
        }
    })
    // 流处理完后发送load事件给消费者
    stream.on('end', function () {
        database.emit('load')
    })
}
let database = new Database('./sub/a.txt')
database._writeStream.write('beep')