核心模块之FS
- FS 是内置的核心模块,提供文件系统操作的API
- fd就是操作系统分配给被打开文件的标识
常见flag 操作符
权限位
文件操作API
- readFile:从指定文件中读取数据
- writeFile:向指定文件中写入数据
- appendFile:追加的方式向指定文件中写入数据
- copyFile:将某个文件中的数据拷贝至另一个文件
- watchFile:对指定文件进行监控
const fs = require('fs')
const path = require('path')
// readFile
// 第一个参数说访问文件,第二个参数是使用什么样的字符编码,第三个参数是一个回调函数
fs.readFile(path.resolve('8-data.txt'), 'utf-8', (err, data) => {
console.log(err) // null(没有错误所以返回这个)
console.log(data) // 徐镜泉学习
})
// writeFile,默认是覆盖操作,会将原本的内容覆盖
// 如果操作的目录不存在的话,会创建一个文件
// 第一个参数是访问的文件,第二个参数是写入的数据,第三个是一个对象,第四个是是一个回调函数
fs.writeFile(
'8-data.txt',
'hello node.js',
{
mode: 438, // 操作权限
flag: 'r+', // 写入方式,重第一个位置写入不清空原来的内容,'w+'就是默认的配置,清空原本的内容在写入
encoding: 'utf-8',
},
(err) => {
if (!err) {
fs.readFile(path.resolve('8-data.txt'), 'utf-8', (err, data) => {
console.log(data) // hello node.js,
})
}
}
)
// appendFile
// 第一个参数是文件路径,第二个参数是写入的内容
// 如果写入的路径不存在,会自动创建一个文件
fs.appendFile('8-data.txt', '徐镜泉学习', (err) => {
console.log('写入成功')
})
// copyFile
// 第一个参数是数据源的路径,第二个参数是复制去那个地方
// 将拷贝的内容替换目标文件内容
fs.copyFile('8-data.txt', 'data.txt', () => {
console.log('拷贝成功')
})
// watchFile
// 第一个参数是监控的文件路径,第二个参数是一个对象(目前表示每20毫秒监控一次)
fs.watchFile('8-data.txt', { interval: 20 }, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
console.log('文件被修改了')
fs.unwatchFile('8-data.txt') // 取消监控
}
})
文件打开与关闭
- open
- close
const fs = require('fs')
const path = require('path')
// open 打开文件
fs.open(path.resolve('data.txt'),'r',(err,fd)=>{
console.log(fd); // 21
})
// close 关闭文件
fs.open(path.resolve('data.txt'),'r',(err,fd)=>{
// 这个时候fd可以用来追踪或者定位当前操作的文件资源
fs.close(fd,err =>{
console.log('关闭成功'); //关闭成功
})
})
大文件读写操作
const fs = require('fs')
let buf = Buffer.alloc(10)
// read : 所谓的读操作就是将数据从磁盘文件中写入到 buffer 中
// data.txt 内容为 1234567890
fs.open('data.txt','r',(err,rfd)=>{
// 第一个参数(fd)就是当前操作的文件,可以通过打开文件的第二个参数追踪到当前操作的文件资源
// 第二个参数(buffer)表示当前的缓冲区
// 第三个参数 (offset) 表示从 buffer 的那个位置开始写入(写入buffer里)
// 第四个参数 (length) 表示当前写入的是长度
// 第五个参数 (position) 表示从对应的字节开始读起
fs.read(rfd,buf,0,3,0,(err,readBytes,data)=>{
console.log(readBytes,'readBytes'); // 3
console.log(data,'data'); // <Buffer 31 32 33 00 00 00 00 00 00 00>
console.log(data.toString(),'string'); // 123
})
fs.read(rfd,buf,0,4,0,(err,readBytes,data)=>{
console.log(readBytes,'readBytes'); // 4
console.log(data,'data'); // <Buffer 31 32 33 34 00 00 00 00 00 00>
console.log(data.toString(),'string'); // 1234
})
fs.read(rfd,buf,1,4,0,(err,readBytes,data)=>{
console.log(readBytes,'readBytes'); // 4
console.log(data,'data'); // <Buffer 00 31 32 33 34 00 00 00 00 00> (因为读操作第一个参数是从1开始的所以第一个为空)
console.log(data.toString(),'string'); // (这里面空格)1234
})
fs.read(rfd,buf,0,3,2,(err,readBytes,data)=>{
console.log(readBytes,'readBytes'); // 3
console.log(data,'data'); // <Buffer 33 34 35 00 00 00 00 00 00 00>
console.log(data.toString(),'string'); // 345
})
})
// write 将缓冲区里面的内容写入的磁盘文件中
buf = Buffer.from('1234567890')
// w表示写操作
fs.open('big.txt', 'w', (err, wfd) => {
// 第一个参数(fd)就是当前操作的文件,可以通过打开文件的第二个参数追踪到当前操作的文件资源
// 第二个参数(buffer)表示当前的缓冲区
// 第三个参数 (offset) 表示从 buffer 的那个位置开始读取数据写入文件
// 第四个参数 (length) 表示当前写入的是长度
// 第五个参数 (position) 表示从对应的字节开始读起 一般不做修改 写 0 就好了
// fs.write(wfd, buf, 0, 3, 0, (err, written, buffer) => {
// // big.txt 文件内容为 123
// console.log(written, 'written'); // 3
// console.log(buffer, 'buffer'); // <Buffer 31 32 33 34 35 36 37 38 39 30>
// console.log(buffer.toString(), 'buffer'); // 1234567890
// fs.close(wfd)
// })
// fs.write(wfd, buf, 0, 4, 0, (err, written, buffer) => {
// // big.txt 文件内容为 1234
// console.log(written, 'written'); // 4
// console.log(buffer, 'buffer'); // <Buffer 31 32 33 34 35 36 37 38 39 30>
// console.log(buffer.toString(), 'buffer'); // 1234567890
// fs.close(wfd)
// })
fs.write(wfd, buf, 1, 3, 0, (err, written, buffer) => {
// big.txt 文件内容为 234
console.log(written, 'written'); // 3
console.log(buffer, 'buffer'); // <Buffer 31 32 33 34 35 36 37 38 39 30>
console.log(buffer.toString(), 'buffer'); // 1234567890
fs.close(wfd)
})
})
文件拷贝自定义实现
const fs = require('fs')
/**
* 01 打开 a 文件,利用 read 将数据保存到 buffer 暂存起来
* 02 打开 b 文件,利用 write 将 buffer 中数据写入到 b 文件中
* */
let buf = Buffer.alloc(10)
// 数据的完全拷贝
const BUFFER_SIZE = buf.length
let readOffset = 0
fs.open('a.txt', 'r', (err, rfd) => {
// a+ 表示追加操作
fs.open('b.txt', 'a+', (err, wfd) => {
function next () {
fs.read(rfd, buf, 0, BUFFER_SIZE, readOffset, (err, readBytes) => {
// readBytes 是每次读取的字节数
if (!readBytes) {
// 如果条件成立说明内容已经读取完毕
fs.close(rfd, () => { })
fs.close(wfd, () => { })
console.log('拷贝完成');
return
}
readOffset += readBytes
// 前面读取多少就写入多少
fs.write(wfd, buf, 0, readBytes, (err, written) => {
next()
})
})
}
next()
})
})
目录操作API(常见)
- access: 判断文件或目录是否具有操作权限
- stat:获取目录及文件信息
- mkdir:创建目录
- rmdir:删除目录
- readdir:读取目录中内容
- unlink:删除指定文件
const fs = require('fs')
// access 判断文件或目录是否具有操作权限
fs.access('a.txt', (err) => {
if (err) {
console.log(err);
} else {
console.log('有操作权限');
}
})
// stat 获取目录及文件信息
fs.stat('a.txt', (err, statObj) => {
console.log(statObj.size) // 大小 48
console.log(statObj.isFile()) // 判断是否文件 true
console.log(statObj.isDirectory()) // 判断是否目录 false
})
// mkdir 创建目录
fs.mkdir('a/b/c', (err) => {
if (!err) {
console.log('创建成功');
} else {
console.log(err); // 会报错,因为当前没有这个目录,父级目录不存在
}
})
// mkdir 创建目录 接受第二个参数以后就不用考虑父级目录存在不存在
fs.mkdir('a/b/c', { recursive: true }, (err) => {
if (!err) {
console.log('创建成功');
} else {
console.log(err); // 会报错,因为当前没有这个目录,父级目录不存在
}
})
// rmdir 删除目录
fs.rmdir('a/b/c', (err) => {
// 目前删除的是c目录
if (!err) {
console.log('删除成功');
} else {
console.log(err);
}
})
// rmdir 删除目录 recursive 为false只能删除空目录
// recursive 为 true 能删除全部目录,整个目录
fs.rmdir('a/b/c', { recursive: true }, (err) => {
// 目前删除的是全部目录
if (!err) {
console.log('删除成功');
} else {
console.log(err);
}
})
// readdir 读取目录中内容
fs.readdir('a', (err, files) => {
console.log(files); // [ 'a.txt', 'b' ]
})
// readdir 读取目录中内容
fs.readdir('a/b', (err, files) => {
console.log(files); // [ 'b.txt' ]
})
// unlink 删除指定文件
fs.unlink('a/a.txt', (err) => {
if (!err) {
console.log('删除成功');
}
})
创建目录之同步实现
const fs = require('fs')
const path = require('path')
/**
* 01 将来调用时需要接收类似于 a/b/c 这样的路径,他们之间是采用 / 去行连接
* 02 利用 / 分隔符将路径进行拆分,将每一项放入一个数组中进行管理 ['a','b','c']
* 03 对上述的数组进行遍历,我们需要拿到每一项,然后与前一项进行拼接 /
* 04 判断一个当前拼接之后的路径是否具有可操作的权限,如果有则证明存在,否则的话就需要执行创建
* */
function makeDirSync (dirPath) {
// 获取当前操作系统的目录路径分隔符
let items = dirPath.split(path.sep)
// console.log(path.sep); // /
// console.log(items);// ['a','b','c']
for (let i = 1; i <= items.length; i++) {
let dir = items.slice(0, i).join(path.sep)
try {
fs.accessSync(dir)
} catch {
fs.mkdirSync(dir)
}
}
}
makeDirSync('a/b/c')
创建目录之异步操作
const fs = require('fs')
const path = require('path')
const { promisify } = require('util') // node里面的工具包
// 第一种写法
// function mkDir (dirPath, cb) {
// let parts = dirPath.split(path.sep)
// let index = 1
// function next () {
// if (index > parts.length) return cb && cb()
// let current = parts.slice(0, index++).join(path.sep)
// fs.access(current, (err) => {
// if (err) {
// // 没有操作权限,需要创建
// fs.mkdirSync(current, next)
// } else {
// next()
// }
// })
// }
// next()
// }
// mkDir('a/b/c', () => {
// console.log('创建成功');
// })
// 第二种写法
// 将 access 与 mkdir 处理成 async 风格
const access = promisify(fs.access)
const mkdir = promisify(fs.mkdir)
async function mkDir (dirPath, cb) {
let parts = dirPath.split(path.sep)
for (let index = 1; index <= parts.length; index++) {
let current = parts.slice(0, index++).join(path.sep)
try {
await access(current)
} catch {
await mkdir(current)
}
}
cb && cb()
}
mkDir('a/b/c', () => {
console.log('创建成功');
})
删除目录之异步实现
const fs = require('fs')
const path = require('path')
/**
* 需求:自定义一个函数,接收一个路径,然后执行删除
* 01 判断当前传入的路径是否为一个文件,是的话直接删除文件即可
* 02 如果当前传入的是一个目录,我们需要继续读取目录中的内容,然后再执行删除操作
* 03 将删除行为定义成一个函数,然后通过递归的方式进行复用
* 04 将当前的名称拼接成在删除时可使用的路径
*/
function myRmdir (dirPath, cb) {
// 判断当前 dirPath 的类型
// 获取目录及文件信息
fs.stat(dirPath, (err, statObj) => {
if (statObj.isDirectory()) {
// 目录 ---> 继续读取
fs.readdir(dirPath, (err, files) => {
// console.log(files); // [ 'a.txt', 'b' ]
let dirs = files.map(item => {
// 变成路径
return path.join(dirPath, item)
})
console.log(dirs); //[ 'a/a.txt', 'a/b' ]
let index = 0
function next () {
if (index === dirs.length) return fs.rmdir(dirPath, cb)
let current = dirs[index++]
myRmdir(current, next)
}
next()
})
} else {
// 文件 ---> 直接删除
fs.unlink(dirPath, cb)
}
})
}
// myRmdir('big.txt', () => {
// console.log('删除成功了');
// })
// myRmdir('a', () => {
// console.log('删除成功了');
// })
myRmdir('c', () => {
console.log('删除成功了');
})