FS 模块
介绍
在 Nodejs 中存在着 Buffer(缓冲区) 和 Stream (数据流)两个非常常见的概念。
Buffer 就是存储的中间变量,目的是方便 CPU 在执行数据存取操作时,可以有一个中间的存储区域。
而流操作类似水流一样,可以通过管道传输数据,以及将数据进行分段。
Buffer 和 Sream 一般操作的都是二进制数据,它们往往与 FS 密不可分。
FS 是 Nodejs 内置核心模块,提供文件系统操作的 API。
所有和文件相关的操作都是通过 FS 模块进行实现的,如文件目录的创建、删除,信息查询、文件读取/写入等。

这里将 FS 模块结构分类两个部分:
- FS 基本操作类:实现文件信息的获取
- 如判断当前是目录还是文件、文件的可读流和可写流的操作、文件的监听行为等
- FS 实例常用 API
- 如文件的打开/关闭、文件的增删改查等
关于系统和文件的前置知识
权限位
这里的“权限”指在当前操作系统内,不同的用户角色对于当前文件所具备的不同权限操作。
文件的权限操作分为三种:
r:read 读权限,读取/查看w:write 写权限,修改文件x:execute 执行权限,执行文件-:无权限
以上权限都不能指定文件的删除权限,删除权限由是否拥有对该文件所在目录的写权限决定。
ex开头的单词缩写时使用第二个字母x而不是e。这是外国长期延续下来的习惯:ex发音近似x。
用八进制数字进行表示:
r:4w:2x:1无权限:0
例如:
rw表示拥有读写权限,数字表示就是6(4+2)。
操作系统中将用户分为三类:
- 文件的所有者:一般指的是当前用户
- 文件的所属组:当前用户所在组
- 其它用户:例如访客用户
文件的权限由以上三类用户依次组成,每类用户的权限由 r、w、x 依次组成,或用一个八进制数字表示。
例如:
774或rwxrwxr--:当前用户和当前用户所属组拥有最大权限,其它用户只拥有读权限777或rwxrwxrwx表示为任意用户分配对当前文件的最大权限。

linux 查看文件权限:
linux 系统执行命令 ls -al 示例:
# ls -al
total 28
drwxr-xr-x 4 root root 4096 Oct 19 2020 .
drwxr-xr-x 4 root root 4096 Aug 16 09:39 ..
drwxr-xr-x 2 root root 4096 Oct 19 2020 css
-rw-rw-rw- 1 root root 4286 Oct 19 2020 favicon.ico
-rw-rw-rw- 1 root root 730 Oct 19 2020 index.html
drwxr-xr-x 2 root root 4096 Oct 19 2020 js
第一列由文件类型和权限组成,d表示当前文件是目录 directory,- 表示当前文件是普通文件。
windows 系统下,文件的权限一般是可读可写但不可执行,即
rw-rw-rw-(666)。
文件标志符
文件系统标志符 flag 表示文件打开的方式。
常见标识符有很多,这里只列举一部分:
a:打开文件进行追加,如果文件不存在,则创建文件r:打开文件进行读取,如果文件不存在,则抛出异常w:打开文件进行写入,如果文件不存在,则创建s:表示以同步模式操作,配合a、r、w进行使用x:表示排它操作,如果路径存在则失败,配合a、w使用+:表示附加操作,配合a、w、r使用,a和w包含写入操作,所以a+和w+附加r读取操作,而r+附加w写入操作。
r+ 和 w+ 的区别是:
- 如果文件不存在,前者抛出异常,后者会创建文件。
w+会将文件内容清空,再写入;r+会读取文件内容,从开头开始覆盖每个字节的内容,不会清空
更多参考 fs 文件系统 | Node.js API 文档 (nodejs.cn)
文件描述符
文件描述符 fd 就是操作系统分配给被打开文件的数字标识。
这个标识用于识别和跟踪每个特定文件。
windows 系统采用了不同但概念类似于文件描述符的机制追踪资源,为了方便用户,Nodejs 抽象了操作系统之间的差异,并为所有打开的文件分类的一个数字文件描述符。
在 Nodejs 中每操作一个文件,文件描述符就会递增一次,并且这个描述符一般是从 3 开始,因为 0、1、2 被标准输入、标准输出、标准错误占用了。
程序首次使用 fs.open() 打开一个文件的时候会获得一个 fd,它就是这个文件的描述符,并且从 3 开始。
更多参考 fs 文件系统 | Node.js API 文档 (nodejs.cn)
fs 总结
- fs 是 Nodejs 中内置的核心模块,所有与文件相关的操作都要通过它的 API 完成
- 代码层面上 fs 分为基本操作类和常用 API
- 文件操作有三个常用概念:权限位、文件标志符、文件描述符
文件操作 API
主要的文件操作就是文件读写、拷贝、监控。
Nodejs 中几乎所有文件 API 操作都有同步和异步两种方式,同步 API 名称比异步 API 名称多个 Sync,如 readFile 对应的同步 API 是 readFileSync,更多可以查看 Nodejs 文档。
常用 API:
- readFile:从指定文件中读取数据
- writeFile:向指定文件中写入数据
- appendFile:向指定文件中追加数据
- copyFile:将某个文件中的数据拷贝到另一个文件
- watchFile:监听指定文件,当文件内容发生修改,触发回调函数(没有对应的同步 API)
readFile、writeFile、appendFile、copyFile都是一次性的操作,例如copyFile会将文件内容一次性获取并放到内存中,然后再一次性写入另一个文件。这些都不适用于大内存的文件操作。
文件操作 API 通常最后一个参数是一个回调函数,Nodejs 中的回调函数通常都是 error-first 错误优先风格的 (err, ...args) => {},即第一个参数是错误信息,后面还是要处理的数据,当没有错误的时候 err 为 null。
测试文件 data.txt:
你好世界
// readFile
const fs = require('fs')
const path = require('path')
// 文件操作通常建议使用绝对路径
// 默认读取的数据是 buffer,通过指定字符编码转化读取的数据
fs.readFile(path.resolve('data.txt'), 'utf-8', (err, data) => {
if (err === null) {
console.log(data) // 你好世界
}
})
// 如果文件不存在,则会报错
fs.readFile(path.resolve('data1.txt'), 'utf-8', (err, data) => {
console.log(err) // 报错
})
// writeFile
const fs = require('fs')
// 所谓写入,就是用新的内容替换原有的内容
fs.writeFile('data.txt', 'Hi', err => {
if (!err) {
fs.readFile('data.txt', 'utf-8', (err, data) => {
console.log(data) // Hi
})
}
})
// 如果写入的文件不存在,会创建该文件
fs.writeFile('data1.txt', 'Hi', err => {
if (!err) {
fs.readFile('data1.txt', 'utf-8', (err, data) => {
console.log(data) // Hi
})
}
})
// writeFile
const fs = require('fs')
// 默认写入,会清空文件内容再写入
// 回调函数仅返回 err
fs.writeFile('data.txt', '你好 Nodejs', err => {
if (!err) {
fs.readFile('data.txt', 'utf-8', (err, data) => {
console.log(data)
})
}
})
// 如果写入的文件不存在,会创建该文件
fs.writeFile('data1.txt', '你好 Nodejs', err => {
if (!err) {
fs.readFile('data1.txt', 'utf-8', (err, data) => {
console.log(data)
})
}
})
// writeFile
const fs = require('fs')
// 第三个参数如果传入的是字符串则表示编码集
// 如果是对象,则可以进行更多配置:
// mode:使用数字设置权限 默认 `0o666`(八进制)
// flag:写入方式,默认 `w`
// encoding:字符编码,默认 `utf8`
fs.writeFile(
'data.txt',
'Hello!',
{
mode: 438, // 默认值 `0o666`(八进制表示) 的十进制表示
flag: 'r+',
encoding: 'utf8' // 与 `utf-8` 等效
},
err => {
if (!err) {
fs.readFile('data.txt', 'utf-8', (err, data) => {
console.log(data) // Hello!世界
})
}
}
)
// appendFile
const fs = require('fs')
fs.appendFile('data.txt', 'Hello', err => {
// 回调函数仅有 err
fs.readFile('data.txt', 'utf8', (err, data) => {
console.log(data) // 你好世界Hello
})
})
// appendFile
const fs = require('fs')
fs.appendFile('data.txt', 'Hello', err => {
// 回调函数仅有 err
fs.readFile('data.txt', 'utf8', (err, data) => {
console.log(data) // 你好世界Hello
})
})
// appendFile
const fs = require('fs')
// 同样可以接收用于配置的第三个参数
fs.appendFile('data.txt', 'Hello', {
flag: 'w' // 现在这个 appendFile 等效于默认的 writeFile
}, err => {
fs.readFile('data.txt', 'utf8', (err, data) => {
console.log(data) // Hello
})
})
// appendFile
const fs = require('fs')
// 第二个参数是目标文件的路径
// 如果目标文件不存在,则会创建文件
fs.copyFile('data.txt', 'data2.txt', err => {
// 回调函数仅接收 err
console.log('拷贝成功')
})
// watchFile
const fs = require('fs')
// watchFile 通过定时轮询文件,检查文件是否发生变化
// 第二个参数如果是对象,则代表配置选项
// interval 表示轮询文件的时间间隔 默认 `5007`
// 回调函数接收 current 和 previous 两个<fs.Stats>类 分别包含文件变化前后的相关信息
fs.watchFile('data.txt', { interval: 200 }, (current, previous) => {
if (current.mtime !== previous.mtime) {
// mtime 表示最新修改时间
console.log('文件内容被修改')
}
})
// 调用 API 修改文件
// 也可以手动打开文件去修改内容
fs.writeFile('data.txt', 'Hello', err => {
console.log('写入内容')
setTimeout(() => {
fs.writeFile('data.txt', 'Hello', err => {
console.log('写入内容相同')
})
}, 1000)
})
// 写入内容
// 文件内容被修改
// 写入内容相同
// 文件内容被修改
// watchFile 监听任务会一直持续,控制台不会退出
// 需要手动停止监听,当删除了所有监听器,程序就会停止运行
// 第二个参数可以指定要删除的监听器(watchFile 的回调函数),如果不指定则删除指定文件的全部监听器
setTimeout(() => {
fs.unwatchFile('data.txt')
}, 3000)
文件操作实现 md 转 html
安装模块
默认样式表
如果你使用 Typora,可以在主题文件夹中选择一个样式表,例如本例使用的 github.css。
示例 markdown 文件
index.md:
# 文件操作实现 md 转 html
## 安装模块
- marked:将 markdown 内容转化成 html 的工具,[官方文档](https://marked.js.org/)
- browser-sync:开启一个 Web 站点打开 html 页面,并实时更新,[官方文档](https://browsersync.io/docs/api)
实现命令
# mdToHtml.js:应用程序文件
# markdown path:markdown 文件路径
node mdToHtml.js <markdown path>
编写程序
const fs = require('fs')
const path = require('path')
const marked = require('marked')
const browserSync = require('browser-sync')
// 01 读取 markdown 和 css 的内容
// 02 将上述读取的内容,替换模板中的占位符,生成最终需要展示的 html 字符串
// 03 将最终 html 字符串写入到指定的 html 文件中
// 04 监听 markdown 文件内容的变化,实时更新 html 内容
// 05 实时显示 html 内容
// html 模板
const temp = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
{{style}}
</style>
</head>
<body>{{content}}</body>
</html>
`
// markdown 文件路径
const mdPath = path.join(__dirname, process.argv[2])
// css 文件路径
const cssPath = path.resolve('github.css')
// html 最终转化的文件路径
// 指定为与 markdown 文件同目录下的同名 html 文件
const htmlPath = mdPath.replace(path.extname(mdPath), '.html')
// 监听 markdown 文件
fs.watchFile(mdPath, (current, previous) => {
if (current.mtime !== previous.mtime) {
// 读取 markdown 内容
fs.readFile(mdPath, 'utf8', (err, data) => {
// 将 markdown 转化为 html
const content = marked(data)
// 读取 css 内容
fs.readFile(cssPath, 'utf8', (err, style) => {
// 替换内容
const html = temp.replace('{{style}}', style).replace('{{content}}', content)
// 写入指定 html 文件
fs.writeFile(htmlPath, html, err => {
console.log('写入成功')
})
})
})
}
})
// 开启服务 显示 html 内容
browserSync.init({
server: {
baseDir: __dirname, // 服务的根目录
index: path.basename(htmlPath) // 指定首页的文件名
},
watch: true // 监听更新
})
执行命令 node mdToHtml.js index.md
本文详细介绍了Node.js内置的FS模块,包括文件的读写、追加、拷贝、监控等操作,以及文件权限位、文件标志符和文件描述符等基础知识。通过示例代码展示了如何使用FS模块进行文件操作,如readFile、writeFile、appendFile等。此外,还提到了如何实现Markdown文件转换为HTML,并利用browser-sync实时更新。
2018

被折叠的 条评论
为什么被折叠?



