Node 学习四、核心模块 fs 之 01 模块介绍、文件一次性操作

本文详细介绍了Node.js内置的FS模块,包括文件的读写、追加、拷贝、监控等操作,以及文件权限位、文件标志符和文件描述符等基础知识。通过示例代码展示了如何使用FS模块进行文件操作,如readFile、writeFile、appendFile等。此外,还提到了如何实现Markdown文件转换为HTML,并利用browser-sync实时更新。

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:4
  • w:2
  • x:1
  • 无权限:0

例如:rw 表示拥有读写权限,数字表示就是 64+2)。

操作系统中将用户分为三类:

  • 文件的所有者:一般指的是当前用户
  • 文件的所属组:当前用户所在组
  • 其它用户:例如访客用户

文件的权限由以上三类用户依次组成,每类用户的权限由 rwx 依次组成,或用一个八进制数字表示。

例如:

  • 774rwxrwxr--:当前用户和当前用户所属组拥有最大权限,其它用户只拥有读权限
  • 777rwxrwxrwx 表示为任意用户分配对当前文件的最大权限。

在这里插入图片描述

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:表示以同步模式操作,配合 arw 进行使用
  • x:表示排它操作,如果路径存在则失败,配合 aw 使用
  • +:表示附加操作,配合 awr 使用,aw包含写入操作,所以 a+w+附加 r 读取操作,而 r+ 附加 w 写入操作。

r+w+ 的区别是:

  • 如果文件不存在,前者抛出异常,后者会创建文件。
  • w+ 会将文件内容清空,再写入;r+ 会读取文件内容,从开头开始覆盖每个字节的内容,不会清空

更多参考 fs 文件系统 | Node.js API 文档 (nodejs.cn)

文件描述符

文件描述符 fd 就是操作系统分配给被打开文件的数字标识。

这个标识用于识别和跟踪每个特定文件。

windows 系统采用了不同但概念类似于文件描述符的机制追踪资源,为了方便用户,Nodejs 抽象了操作系统之间的差异,并为所有打开的文件分类的一个数字文件描述符。

在 Nodejs 中每操作一个文件,文件描述符就会递增一次,并且这个描述符一般是从 3 开始,因为 012 被标准输入、标准输出、标准错误占用了。

程序首次使用 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)

readFilewriteFileappendFilecopyFile 都是一次性的操作,例如 copyFile 会将文件内容一次性获取并放到内存中,然后再一次性写入另一个文件。这些都不适用于大内存的文件操作。

文件操作 API 通常最后一个参数是一个回调函数,Nodejs 中的回调函数通常都是 error-first 错误优先风格的 (err, ...args) => {},即第一个参数是错误信息,后面还是要处理的数据,当没有错误的时候 errnull

测试文件 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

安装模块

  • marked:将 markdown 内容转化成 html 的工具,官方文档
  • browser-sync:开启一个 Web 站点打开 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值