fs文件基础用法

294 阅读4分钟

fs (File system) 是文件系统模块,用于操作文件和目录。

支持同步 (sync) 或者异步 (async/callback) 调用,其中同步调用会阻塞主线程,异步调用不会阻塞。

下面分别是 3 种写法,日常开发中常用 同步操作 和 Promise形式异步操作。

import fs from 'fs'

// 同步读取
const syncData = fs.readFileSync('./test.txt', 'utf-8')
console.log('====sync read====')
console.log(syncData)

// 回调形式 异步读取
fs.readFile('./test.txt', 'utf-8', (err, callbackData) => {
  if (!err) {
    console.log('====callback read====')
    console.log(callbackData)
  }
})

// promise形式 异步读取
fs.promises.readFile('./test.txt', 'utf-8').then((promiseData) => {
  console.log('====promise read====')
  console.log(promiseData)
})

// promise 形式还可以是如下写法(常用)
// import fs from 'fs/promises' //注意:使用promise形式的时候必须引用该模块
// fs.readFile('./test.txt', 'utf-8').then((promiseData) => {
//   console.log('====promise read====')
//   console.log(promiseData)
// })

1 文件操作

1.1 读取文件

fs.readFileSync 基础用法如下:

  • 参数 1:设置要读取的文件路径 (相对或者绝对);
  • 参数 2:设置读取的编码格式。
import fs from 'fs'

const txtContent = fs.readFileSync('./test.txt', 'utf-8')

以二进制形式读取,操作:

const buf = fs.readFileSync('./test.txt')

// 打印Buffer大小
console.log(buf.length)
// 修改前2个字符
buf.write('gg')

// 输出修改后的内容
console.log(buf.toString())

1.2 写入文件

基础用法如下 fs.writeFileSync:

  • 参数 1:输出文件路径;
  • 参数 2:输出内容;
  • 参数 3 (可选):编码格式。
fs.writeFileSync('./newTest.txt', 'hello world')

修改前的文件:

修改后的文件:

写入二进制文件 (读取一个图片,然后输出到一个新的位置)

// 读取一个图片
const imgBuf = fs.readFileSync('./logo.png')
console.log('isBuffer', Buffer.isBuffer(imgBuf), 'bufferSize', imgBuf.length)

// 写入到新文件
fs.writeFileSync('newLogo.png', imgBuf, 'binary')

修改前img目录文件:

修改后的img目录文件:

运行后的输出:

1.3 获取文件信息

通过 fs.statSync 获取文件或者目录的基本信息。

import fs from 'fs'

console.log(fs.statSync('./test.txt'))
console.log(fs.statSync('./img'))

返回的对象属性如下:

常用字段的意义如下:

返回的对象上还包含可直接调用的方案,用于判断文件类型:

const fileInfo = fs.statSync('./test.txt')
// 判断是文件还是目录
console.log(fileInfo.isFile(), fileInfo.isDirectory())

const dirInfo = fs.statSync('./img')
// 判断是文件还是目录
console.log(dirInfo.isFile(), dirInfo.isDirectory())

try {
  // 查询一个不存在的文件/目录信息(会抛出异常,需要自行捕获)
  fs.statSync('not_exist.txt')
} catch (e) {
  console.log('文件不存在')
}

1.4 追加输出

使用 fs.appendFileSync 向文件末尾追加写入内容。

// 引入文件系统模块
import fs from 'fs';

// 使用 fs.appendFileSync() 方法向指定文件追加内容
// 参数1:指定文件路径
// 参数2:要追加的内容
fs.appendFileSync('test.txt', 'Hello World2!');

修改前的文件:

修改后的文件:

1.5 移动/重命名文件

fs.renameSync 方法用于文件移动,当然也可以是重命名文件。

下面是文件重命名示例。

import fs from 'fs'

fs.renameSync('test.txt', 'test2.txt')

修改前的目录:

修改后端的目录:

下面是移动文件示例:

fs.renameSync('test2.txt', 'test-dir/test2.txt')

移动后的文件出现的位置:

1.6 删除文件

fs.unlinkSync 和 fs.rmSync 都可用于单文件删除。

import fs from 'fs'

fs.unlinkSync('test-dir/test2.txt')
// fs.rmSync('test-dir/test2.txt')

删除文件前的目录:

删除文件后的目录:

当然后者还支持删除目录,递归删除子文件/目录等。

// 删除 test-dir 目录(包含其子文件)
fs.rmSync('test-dir', { recursive: true })

删除前的目录:

删除后的目录:

2 目录操作

2.1 读取目录所有文件

通过 fs.readdirSync 获取目录下的文件信息。

import fs from 'fs'

const files = fs.readdirSync('test-dir')

console.log(files)

被读取的目录:

读取目录后的结果:

可通过指定第二个参数 withFileTypes:true 使返回结果包含类型:

const files2 = fs.readdirSync('test-dir', { withFileTypes: true })
console.log(files2.map((f) => ({ name: f.name, isDirectory: f.isDirectory() })))

2.2 创建目录

使用 fs.mkdirSync 创建目录,可通过设置 recursive:true 来递归创建多级目录。

fs.mkdirSync('test-dir/a/b/c/d', { recursive: true })

创建新的目录前:

创建新的目录后:

2.3 删除目录

可以使用 fs.rmdirSync 删除目标目录,recursive: true 表明删除包含其子目录。

fs.rmdirSync('test-dir/a', { recursive: true })

删除目录前:

删除目录后:

也可以使用上面提到的 fs.rmSync:

fs.rmSync('test-dir/a', { recursive: true })

2.4 监听目录变更

利用 fs.watch 即可实现。

import fs from 'fs'
// 监听当前目录下所有的文件和子目录中的文件
fs.watch('./', { recursive: true }, (eventType, filename) => {
  console.log(`File '${filename}' has changed: ${eventType}`)
})

小结

本小节先简单介绍了三种调用文件系统 API 的方式:

  • 同步 (Sync):例如 fs.readFileSync,会阻塞主线程;
  • 异步 (Async/Callback):fs.promises.readFile,fs.readFile,不会阻塞主线程。

日常使用中推荐使用 fs/promise 的方式。

紧接着分别介绍了文件和目录的常规的 CRUD 方法,

最后综合运用上面介绍的方法,解决一个常见场景问题获取指定目录下所有文件的绝对路径