node - fs模块

94 阅读5分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

fs是File System的缩写,表示文件系统

对于任何一个为服务器端服务的语言或者框架通常都会有自己的文件系统,

因为服务器需要将各种数据、文件等放置到不同的地方,

比如用户数据可能大多数是放到数据库中或者某些配置文件或者用户资源(图片、音视频)都是以文件的形式存在于操作系统上

借助于Node帮我们封装的文件系统,我们可以在任何的操作系统(window、Mac OS、Linux)上面直接去操作文件

操作文件

操作文件这类异步API,node提供给了我们三种不同的调用方式

  1. 同步操作文件:代码会被阻塞,不会继续执行
const fs = require('fs')

// 同步读取文件,会阻塞后续代码的执行
const res = fs.readFileSync('./index.txt')

// 默认情况下,文件会被作为二进制文件进行读取和解析
// 所以读取到的数据 实际会被当做 Buffer 进行输出
console.log(res)

console.log('后续代码会被阻塞')
const fs = require('fs')

const res = fs.readFileSync('./index.txt')

// 1. 调用buffer实例的toString方法可以将buffer转换为utf-8编码格式的string
console.log(res.toString())

console.log('后续代码会被阻塞')
const fs = require('fs')

// readFile还有第二个参数,是对应的配置对象
// 可以通过encoding选项来设置对应的文件编解码格式
// ps: utf-8 === utf8
const res = fs.readFileSync('./index.txt', {
  encoding: 'utf-8'
})

console.log(res)

console.log('后续代码会被阻塞')
  1. 异步回调函数操作文件:代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行
const fs = require('fs')

fs.readFile('./index.txt', {
  encoding: 'utf-8'
}, (err, data) => {
  if (err) {
    // 如果err有值 表示出现了错误 err是对应的错误对象 如果没有错误,那么对应的值就是null
    // 如果err无值 表示读取成功 此时读取到的数据会做为回调的第二个参数被传入
    console.error('读取失败');
  } else {
    console.log(data)
  }
})

console.log('后续代码不会被阻塞')
  1. 异步Promise操作文件:代码不会被阻塞,通过 fs.promises 调用方法操作,会返回一个Promise,可以通过then、 catch进行处理
const fs = require('fs')

fs.promises.readFile('./index.txt', {
  encoding: 'utf-8'
}).then(data => console.log(data))
.catch(err => console.log(err))

console.log('后续代码不会被阻塞')

文件描述符

在常见的操作系统上,对于每个进程,内核都维护着一张当前打开着的文件和资源的表格(也就是文件描述符和对应资源之间的映射表)

每个打开的文件都分配了一个称为文件描述符的简单的数字标识符

在系统层,所有文件系统操作都使用这些文件描述符来标识和跟踪每个特定的文件

Windows 系统使用了一个虽然不同但概念上类似的机制来跟踪资源

为了简化用户的工作,Node.js 抽象出操作系统之间的特定差异,并为所有打开的文件分配一个数字型的文件描述符

const fs = require('fs')

// fd - file descriptors - 文件描述符
// 在readFile或writeFile的时候
// 第一个参数 除了可以传递对应的文件路径外,也可以传递对应的文件描述符
fs.open('./index.txt', (err, fd) => {
  if (err) {
    console.error(err)
  }

  // fsstas - file status - 读取文件想关的元数据信息
  // fstats 的第一个参数 必须是 文件描述符
  // 对于node的回调,第一个参数一般都是错误对象,第二个参数才是对应的数据
  fs.fstat(fd, (err, stats) => {
    if (err) {
      console.error(srr)
    }

    console.log(stats)

    // 使用open方法打开的文件默认情况下不会像readFile或writeFile那样自动关闭,需要手动进行关闭
    // 虽然node进程被销毁后,所有通过该node进程打开的文件都会被自动关闭
    // 但是因为一般node主要是在服务端提供对应的服务的,一般对应的node进程并不会被关闭
    // 所以手动关闭对应的文件,是一个非常良好的习惯
    fs.close(fd)
  })
})

文件读写

const fs = require('fs')

// 参数一 - 文件路径 - 不存在的时候 会自动新建对应的文件
// 参数二 - 需要写入的内容
// 参数三 - 配置对象 - 可以省略
// 参数四 - 回调函数
fs.writeFile('./foo.txt', 'Hello World', err => {
  if (err) {
    console.log('文件写入失败')
  }

  // 参数一 - 文件路径
  // 参数二 - 配置对象 - 可以省略
  // 参数三 - 回调函数
  fs.readFile('./foo.txt', (err, data) => {
    if (err) {
      console.error(err)
    } else {
      console.log(data.toString())
    }
  })
})

flag

使用flag来标识对应的文件的读写方式

说明
w打开文件写入,写入时候的默认值,如果不存在则创建文件
w+打开文件进行读写(可读可写),如果不存在则创建文件
r打开文件读取,读取时的默认值,如果不存在那么抛出异常
r+打开文件进行读写,如果不存在那么抛出异常
a打开要写入的文件,将流放在文件末尾。如果不存在则创建文件
a+打开文件以进行读写(可读可写),将流放在文件末尾。如果不存在则创建文件

文件夹操作

创建文件

const fs = require('fs')

fs.mkdir('./foo', err => {
  if (err) {
    console.log(err)
  }
})

获取文件夹内容

# 存在文件夹 bak 其对应目录结构如下
.
├── a
│   └── a.txt
├── b
│   └── b.txt
└── index.txt
const fs = require('fs')

fs.readdir('./bak', (err, files) => {
  if (err) {
    console.log(err)
  } else {
    // 默认情况下 会以字符串数组的形式 输出第一层下所对应的文件和文件夹
    console.log(files) // => [ 'a', 'b', 'index.txt' ]
  }
})
const fs = require('fs')

// readdir 第二个参数可以传递一个可以省略的配置对象
// withFileTypes: boolean
//   - false 以字符串形式输出文件或文件夹名称
//   - true 以对象形式输出 包含名称和文件类型
fs.readdir('./bak', {
  withFileTypes: true
} ,(err, files) => {
  if (err) {
    console.log(err)
  } else {
    for (const file of files) {
      // 如果以withFileTypes形式输出对应的文件信息
      // 那么对应的每一个对象都是Dirent的实例对象
      // 所以可以通过其isDirectory方法 判断当前实例对应的是否是一个文件夹
      console.log(file.isDirectory())
    }
  }
})
const fs = require('fs')

// 递归读取文件夹下所有的文件
function readFile(path) {
  fs.readdir(path, {
    withFileTypes: true
  }, (err, files) => {
    if (err) {
      console.error(err)
    } else {
      for (const file of files) {
        if (file.isDirectory()) {
          readFile(`${path}/${file.name}`)
        } else {
          console.log(file.name)
        }
      }
    }
  })
}

readFile('./bak')

重命名

const fs = require('fs')

// rename方法 不仅仅可以对文件夹进行重命名操作,也可以对文件进行重命名操作
// 参数一 - 旧文件名
// 参数二 - 新文件名
// 参数三 - 回调函数
fs.rename('./foo', './bak', err => console.log(err))
fs.rename('./foo.txt', './bak.exe', err => console.log(err))