FS 模块
FS是内置的核心模块 提供文件系统操作的API
FS 的基本操作类
权限位、标识符、文件描述符 用户对于文件所具备的操作权限
NodeJs 中 flag 表示文件的操作方式
常见 flag 操作符
- r: 表示可读
- w:表示可写
- s: 表示同步
- +:表示执行相反操作
- x: 表示排它操作
- a: 表示追加操作
fd 就是操作系统分配给被打开文件的标识
总结
- fs 是 Nodejs 中内置核心模块
- 代码层面上 fs 分为基本操作类和常用 API
- 权限位、标识符、操作符
FS 的基本API
文件操作API
- readFile:从指定文件中读取数据
- writeFile:向指定文件写入数据
- appendFile:追加的方式向指定文件写入数据
- copyFile:将文件中数据拷贝至另一文件
- watchFile:对指定文件进行监控
const fs = require('fs')
const path = require('path')
readFile
fs.readFile(path.resolve('data1.txt'), 'utf-8', (err, data) => {
console.log(err)
if (!null) {
console.log(data)
}
})
// [Error: ENOENT: no such file or directory, open 'C:\Users\Zhao\Desktop\04FS\data1.txt'] {
// errno: -4058,
// code: 'ENOENT',
// syscall: 'open',
// path: 'C:\\Users\\Zhao\\Desktop\\04FS\\data1.txt'
// }
// null
// 1234567890
writeFile
fs.writeFile('data.txt', '123', {
mode: 438,
flag: 'w+',
encoding: 'utf-8'
}, (err) => {
if (!err) {
fs.readFile('data.txt', 'utf-8', (err, data) => {
console.log(data)
})
}
})
// 123
appendFile
fs.appendFile('data.txt', 'hello node.js',{}, (err) => {
console.log('写入成功')
})
copyFile
fs.copyFile('data.txt', 'test.txt', () => {
console.log('拷贝成功')
})
watchFile
fs.watchFile('data.txt', {interval: 20}, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
console.log('文件被修改了')
fs.unwatchFile('data.txt') // 用于停止监控
}
})
文件操作实现 md 转 html
const fs = require('fs')
const path = require('path')
const marked = require('marked') // JavaScript 编写的全功能 Markdown 解析和编译器
const browserSync = require('browser-sync') // 浏览器同步测试工具
-
读取 md 和 css 内容
-
将上述读取出来的内容替换占位符,生成一个最终需要展的 Html 字符串
-
将上述的 Html 字符写入到指定的 Html 文件中
-
监听 md 文档内容的变经,然后更新 html 内容
-
使用 browser-sync 来实时显示 Html 内容
let mdPath = path.join(__dirname, process.argv[2]) // __dirname 根目录 process.argv[2] 命令行下标为2的
let cssPath = path.resolve('index.css')
let htmlPath = mdPath.replace(path.extname(mdPath), '.html')
fs.watchFile(mdPath, (curr, prev) => {
if(curr.mtime !== prev.mtime){
fs.readFile(mdPath, 'utf-8', (err, mdData) => {
// md => html
let htmlStr = marked(mdData)
fs.readFile(cssPath, 'utf-8', (err, cssData) => {
let retHtml = temp.replace('{{content}}', htmlStr).replace('{{style}}', cssData)
// 将上述的内容写入到指定的 html 文件中,用于在浏览器里进行展示
fs.writeFile(htmlPath, retHtml, err => {
console.log('html 生成成功了')
})
})
})
}
})
browserSync.init({
browser: '',
server: __dirname,
watch: true,
index: path.basename(htmlPath)
})
onst temp = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 1000px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 750px) {
.markdown-body {
padding: 15px;
}
}
{{style}}
</style>
</head>
<body>
<div class="markdown-body">
{{content}}
</div>
</body>
</html>
`
// 命令行: node .\index.js index.md
文件的打开与关闭
fs.open( filename, flags, mode, callback )
-
filename: 它保存要读取的文件名或完整路径(如果存储在其他位置)。
-
flag: 必须在其中打开文件的操作。
-
mode: 设置文件的模式
-
callback: 这是在读取文件后调用的回调函数。它带有两个参数:
- err: 如果发生任何错误。
- data: 文件内容。打开操作执行后调用。
fs.open(path.resolve('data.txt'), 'r', (err, fd) => {
console.log(fd)
})
// 文件内容
fs.close( fd, callback )
-
fd: 表示要关闭的文件的文件描述符。
-
callback: 该方法执行时将调用该函数。
- err: 方法失败,将抛出此错误。
fs.open('data.txt', 'r', (err, fd) => {
console.log(fd)
fs.close(fd, err => {
console.log('关闭成功')
})
})
大文件读写操作
read(fd, buf, offset, length, position, callback) 读操作就是将数据从磁盘文件中写入到 buffer 中
- fd 定位当前被打开的文件
- buf 用于表示当前缓冲区
- offset 表示当前从 buf 的哪个位置开始执行写入
- length 表示当前次写入的长度
- position 表示当前从文件的哪个位置开始读取
- callback: 该方法执行时将调用该函数。
let buf = Buffer.alloc(10)
fs.open('data.txt', 'r', (err, rfd) => {
console.log(rfd)
fs.read(rfd, buf, 1, 4, 3, (err, readBytes, data) => {
console.log(readBytes)
console.log(data)
console.log(data.toString())
})
})
write(fd, buf, offset, length, position, callback) 将缓冲区里的内容写入到磁盘文件中
let buf = Buffer.from('1234567890')
fs.open('b.txt', 'w', (err, wfd) => {
fs.write(wfd, buf, 2, 4, 0, (err, written, buffer) => {
console.log(written, '----')
fs.close(wfd)
})
})
文件拷贝自定义实现
const fs = require('fs')
let buf = Buffer.alloc(10)
- 打开 a 文件,利用 read 将数据保存到 buffer 暂存起来
- 打开 b 文件,利用 write 将 buffer 中数据写入到 b 文件中
// 打开指定的文件
fs.open('a.txt', 'r', (err, rfd) => {
// 03 打开 b 文件,用于执行数据写入操作
fs.open('b.txt', 'w', (err, wfd) => {
// 02 从打开的文件中读取数据
fs.read(rfd, buf, 0, 10, 0, (err, readBytes) => {
// 04 将 buffer 中的数据写入到 b.txt 当中
fs.write(wfd, buf, 0, 10, 0, (err, written) => {
console.log('写入成功')
})
})
})
})
// 数据的完全拷贝
fs.open('a.txt', 'r', (err, rfd) => {
fs.open('b.txt', 'a+', (err, wfd) => {
fs.read(rfd, buf, 0, 10, 0, (err, readBytes) => {
fs.write(wfd, buf, 0, 10, 0, (err, written) => {
fs.read(rfd, buf, 0, 5, 10, (err, readBytes) => {
fs.write(wfd, buf, 0, 5, 10, (err, written) => {
console.log('写入成功')
})
})
})
})
})
})
// 递归拷贝 每次拷贝10
const BUFFER_SIZE = buf.length
let readOffset = 0
fs.open('a.txt', 'r', (err, rfd) => {
fs.open('b.txt', 'w', (err, wfd) => {
function next () {
fs.read(rfd, buf, 0, BUFFER_SIZE, readOffset, (err, readBytes) => {
if(!readBytes) {
// 如果条件成立,说明内容已经读取完毕
fs.close(rfd, ()=>{})
fs.close(wfd, ()=>{})
return
// 拷贝完成
}
readOffset += readBytes
fs.write(wfd, buf, 0, readBytes, (err, written) => {
next()
})
})
}
next()
})
})
目录操作API
- access 判断对文件或目录是否具有操作权限
- stat 获取文件及目录信息
- mkdir 创建目录
- rmdir 删除目录
- readdir 读取目录中内容
- unlink 删除指定文件
access
fs.access('a.txt', (err) => {
if (err) {
console.log(err)
} else {
console.log('有操作权限')
}
})
// 有操作权限
stat
fs.stat('a.txt', (err, statObj) => {
console.log(statObj)
})
// {
// dev: 273018801,
// mode: 33206,
// nlink: 1,
// uid: 0,
// gid: 0,
// rdev: 0,
// blksize: 4096,
// ino: 1407374883619733,
// size: 108,
// blocks: 0,
// atimeMs: 1683869116264.2441,
// mtimeMs: 1605512042441.8066,
// ctimeMs: 1683621786820.773,
// birthtimeMs: 1683621786820.773,
// atime: 2023-05-12T05:25:16.264Z,
// mtime: 2020-11-16T07:34:02.442Z,
// ctime: 2023-05-09T08:43:06.821Z,
// birthtime: 2023-05-09T08:43:06.821Z
// }
mkdir
fs.mkdir('a/b/c', {recursive: true}, (err) => {
if (!err) {
console.log('创建成功')
}else{
console.log(err)
}
})
// 创建成功
rmdir
fs.rmdir('a', {recursive: true}, (err) => {
if (!err) {
console.log('删除成功')
} else {
console.log(err)
}
})
// 删除成功
readdir
fs.readdir('a/b', (err, files) => {
console.log(files)
})
unlink
fs.unlink('a/a.txt', (err) => {
if (!err) {
console.log('删除成功')
}
})
// 删除成功
模拟实现创建目录(同步)
const fs = require('fs')
const path = require('path')
- 将来调用时需要接收类似于 a/b/c ,这样的路径,它们之间是采用 / 去行连接
- 利用 / 分割符将路径进行拆分,将每一项放入一个数组中进行管理 ['a', 'b', 'c']
- 对上述的数组进行遍历,我们需要拿到每一项,然后与前一项进行拼接 /
- 判断一个当前对拼接之后的路径是否具有可操作的权限,如果有则证明存在,否则的话就需要执行创建
function makeDirSync (dirPath) {
let items = dirPath.split(path.sep)
for(let i = 1; i <= items.length; i++) {
let dir = items.slice(0, i).join(path.sep)
try {
fs.accessSync(dir)
} catch (err) {
fs.mkdirSync(dir)
}
}
}
makeDirSync('a\\b\\c')
模拟实现创建目录(异步)
const fs = require('fs')
const path = require('path')
function mkDir (dirPath, cb) {
let parts = dirPath.split('/')
let index = 1
function next () {
if (index > parts.length) return cb && cb()
let current = parts.slice(0, index++).join('/')
fs.access(current, (err) => {
if (err) {
fs.mkdir(current, next)
}else{
next()
}
})
}
next()
}
mkDir('a/b/c', () => {
console.log('创建成功')
})
处理成 async... 风格
const { promisify } = require('util')
const access = promisify(fs.access)
const mkdir = promisify(fs.mkdir)
async function myMkdir (dirPath, cb) {
let parts = dirPath.split('/')
for(let index = 1; index <= parts.length; index++) {
let current = parts.slice(0, index).join('/')
try {
await access(current)
} catch (err) {
await mkdir(current)
}
}
cb && cb()
}
myMkdir('a/b/c', () => {
console.log('创建成功')
})
模拟实现递归删除目录
需求:自定义一个函数,接收一个路径,然后执行删除
const fs = require('fs')
const path = require('path')
- 判断当前传入的路径是否为一个文件,直接删除当前文件即可
- 如果当前传入的是一个目录,我们需要继续读取目录中的内容,然后再执行删除操作
- 将删除行为定义成一个函数,然后通过递归的方式进行复用
- 将当前的名称拼接成在删除时可使用的路径
function myRmdir (dirPath, cb) {
// 判断当前 dirPath 的类型
fs.stat(dirPath, (err, statObj) => {
if (statObj.isDirectory()) {
// 目录---> 继续读取
fs.readdir(dirPath, (err, files) => {
let dirs = files.map(item => {
return path.join(dirPath, item)
})
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('tmp', () => {
console.log('删除成功了')
})