阅读 227

NodeJS之fs文件系统模块的理解

fs文件系统模块是NodeJS核心模块之一,用于对系统文件及目录进行读写操作,官方文档讲解了fs模块主要提供三种API机制以供开发者使用。

  • 基于Promise的方式

fs/promises API 提供了返回 promise 的异步的文件系统方法。

注意:Promise API 使用底层的 Node.js 线程池在事件循环线程之外执行文件系统操作。 这些操作不是同步的也不是线程安全的。 对同一文件执行多个并发修改时必须小心,否则可能会损坏数据。

  • 基于callback回调方式

回调的 API 异步地执行所有操作,不会阻塞事件循环,然后在完成或错误时调用回调函数。

注意:回调的 API 使用底层 Node.js 线程池在事件循环线程之外执行文件系统操作。 这些操作不是同步的也不是线程安全的。 对同一文件执行多个并发修改时必须小心,否则可能会损坏数据。

  • 基于同步的方式

同步的 API 同步地执行所有操作,阻塞事件循环,直到操作完成或失败。

本篇文章以回调的API为例,分析总结fs模块常用文件操作API的用法及注意事项。

1. 权限相关

1. 1 fs.access(path[, mode], callback)

测试用户对 path 指定的文件目录的权限。

参数:

  • path: string | Buffer | url

  • mode: fs.constants.F_OK(存在性) | fs.constants.R_OK(可读性) | fs.constants.W_OK(可写性)

  • callback: 回调函数,传入值为可能错误的err

import fs from 'fs'; import path from 'path'; const rootDir = path.resolve(); const testPath = path.join(rootDir, 'files', 'abc.text'); fs.access(testPath, fs.constants.R_OK, (err) => {   if (err) {     console.log(err)   } else {     console.log('存在文件')   } }) 复制代码

注意:path不存在或者没有权限都将调用回调函数并传入err对象。

1. 2 fs.chmod(path, mode, callback)

异步地更改文件的权限。

参数:

  • path: string | Buffer | url

  • mode: string | integger; mode参数说明参考

  • callback: Function(err)

1. 3 fs.fchmod(fd, mode, callback)

设置文件的权限

参数:

  • fd: integger

  • mode: string | integger; mode参数说明参考

  • callback: Function(err)

注意:此方法与fs.chmod()方法唯一不同点在于参数fd,通过文件描述符修改文件权限。其它参数一致。

1. 4 fs.chown(path, uid, gid, callback)

异步地更改文件的所有者和群组

参数:

  • path:string | Buffer | url

  • uid: integger

  • gid: integger

  • callback: 回调函数

1. 5 fs.fchown(fd, uid, gid, callback)

设置文件的所有者

参数:

  • fd:integger

  • uid: integger

  • gid: integger

  • callback: 回调函数

注意:次方法与fs.chown()方法的区别主要在第一个参数的区别。fd为文件描述符。

2. 新建相关

2.1 fs.mkdir(path[, options], callback)

异步地创建目录。

参数:

  • path: string | Buffer | url

  • options: integer | Object

  • callback: Function(err, path?)

fs.mkdir(path.join(path.resolve(), 'cssFiles'), {recursive: true}, (err,path) => {   if (err) {     return;   }   // path仅在options recursive: true时存在   console.log(path) }) 复制代码

3. 文件信息

3. 1 fs.stat(path[, options], callback)

获取文件状态信息

参数:

  • path: string | Buffer | url

  • options: Object

  • callback:回调函数

import fs from 'fs'; import path from 'path'; const rootDir = path.resolve(); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.stat(testPath,(err,stats) => {   if (err) {     console.log(err);     return;   }   console.log(stats);   {       dev: 4271079436,       mode: 33206,       nlink: 1,       uid: 0,       gid: 0,       rdev: 0,       blksize: 4096,       ino: 281474980389124,       size: 1360,       blocks: 8,       atimeMs: 1628750429976.6963,       mtimeMs: 1628750410741.5435,       ctimeMs: 1628750410741.5435,       birthtimeMs: 1628750410738.552,       atime: 2021-08-12T06:40:29.977Z,       mtime: 2021-08-12T06:40:10.742Z,       ctime: 2021-08-12T06:40:10.742Z,       birthtime: 2021-08-12T06:40:10.739Z     } }) 复制代码

3. 2 fs.fstat(fd[, options], callback)

使用文件描述符获取文件描述信息

注意:次方法与fs.stat()方法区别在于第一个参数fd,文件描述符

4. 打开访问相关

4.1 fs.open(path[, flags[, mode]], callback)

异步地打开文件

import fs from 'fs'; import path from 'path'; const rootDir = path.resolve(); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.open(testPath, (err, fd) => {   if (err) {     console.log(err);     return;   }   console.log(fd); // 3 }) 复制代码

4.2 fs.opendir(path[, options], callback)

异步地打开目录

import fs from 'fs'; import path from 'path'; const rootDir = path.resolve(); const testDir = path.join(rootDir, 'files'); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.opendir(testDir, async(err, files) => {   if (err) {     console.log(err);     return;   }   for await (const f of files) {     console.log(f)   } }) // 打印结果: // Dirent { name: 'abc.css', [Symbol(type)]: 1 } // Dirent { name: 'text.txt', [Symbol(type)]: 1 } 复制代码

4.3 fs.close(fd[, callback])

关闭文件描述符。

5. 读取相关

5.1 fs.read(fd)

从 fd(文件描述符) 指定的文件中读取数据 此方法有两种使用方式:

  • fs.read(fd, buffer, offset, length, position, callback)

  • fs.read(fd, [options,] callback)

import fs from 'fs'; import path from 'path'; import {Buffer} from 'buffer'; const rootDir = path.resolve(); const testDir = path.join(rootDir, 'files'); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.stat(testPath, (err1, stat) => {   if (err1) {     return;   }   fs.open(testPath, (err2, fd) => {     if (err2) {       return;     }     const len = stat.size;     const offset = 0;     const filePosition = 0;     // 创建一个与文件大小相同的缓冲区     const readBuffer = Buffer.alloc(len);     // 读取文件全部内容     fs.read(fd, readBuffer, offset, len, filePosition, (err3, bytesRead, buffer) => {       if (err3) {         return;       }       console.log(bytesRead)       console.log(buffer.toString());     })   }) }) 复制代码

5.2 fs.readFile(path[, options], callback)

异步地读取文件的全部内容。

参数:

  • path: string | Buffer | url

  • options: Object | string

  • callback:Function(err, data)

参数说明:

  • 如果未指定编码,则返回原始缓冲区。

  • 如果 options 是字符串,则它指定编码:如 utf8, base64

import fs from 'fs'; import path from 'path'; const rootDir = path.resolve(); const testDir = path.join(rootDir, 'files'); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.readFile(testPath, 'utf8', (err, data) => {   if (err) {     return;   }   console.log(data) }) 复制代码

read()与readFile()对比说明

  • fs.readFile()方法是对fs.read()方法的进一步封装,以方便的读取文件的全部内容。

  • 相比fs.readFile()方法,使用fs.read()方法读取文件的全部内容就要复杂的多。首先要用fs.stat或fs.fstat判断文件的大小,然后使用fs.open()创建文件描述符,最后再使用fs.read()方法读取文件内容。

  • fs.read更适合读取部分文件数据内容。

5.3 fs.readdir(path[, options], callback)

异步读取目录的内容

参数:

  • path: string | Buffer | url

  • options: string | Object

  • callback: Function(err, files)

import fs from 'fs'; import path from 'path'; import {Buffer} from 'buffer'; const rootDir = path.resolve(); const testDir = path.join(rootDir, 'files'); const testPath = path.join(rootDir, 'files', 'text.txt'); fs.readdir(testDir, (err, files) => {   if (err) {     return;   }   console.log(files) }) // 结果 [ 'abc.css', 'text.txt' ] Dirent { name: 'abc.css', [Symbol(type)]: 1 } Dirent { name: 'text.txt', [Symbol(type)]: 1 } 复制代码

6. 写入相关

6.1 fs.write()

将数据写入指定fd

与fs.read()相似,同样有两种使用方式

  • fs.write(fd, buffer[, offset[, length[, position]]], callback)

  • fs.write(fd, string[, position[, encoding]], callback)

6.2 fs.writeFile(file, data[, options], callback)
fs.writeFile(path.join(path.resolve(), 'files', 'newText.txt'), 'hello fs.wirteFile2', (err)=> {   if (err) {     throw err;     return;   }   console.log('数据写入成功') }) 复制代码

注意说明

  • 如果file不存在则新建file

  • 如果file存在则覆盖file原有数据

  • writeFile是对write的封装

6.3 fs.appendFile(path, data[, options], callback)

异步地将数据追加到文件

fs.appendFile(path.join(path.resolve(), 'files', 'newText.txt'), '这是一段新追加文本内容', (err) => {   if (err) {     return;   }   console.log('数据追加成功') }) 复制代码

注意说明

  • 如果file不存在则新建file

  • 相比fs.writeFile不同,file存在则追加内容,不进行覆盖。

7. 删除复制相关

7.1 fs.rm(path[, options], callback)

异步地删除文件和目录

// options recursive: true执行递归删除 fs.rm(path.join(path.resolve(), 'cssFiles'), {recursive: true}, (err) => {   if (err) {     return;   }   console.log('删除成功') }) 复制代码

7.2 fs.rmdir(path[, options], callback)

异步的删除目录

// 仅能删除空文件夹 fs.rmdir(path.join(path.resolve(), 'cssFiles'), (err) => {   if (err) {     console.log(err)     return;   }   console.log('删除成功') }) // 递归删除-传入参数options recursive: true fs.rmdir(path.join(path.resolve(), 'cssFiles'), {recursive: true}, (err) => {   if (err) {     console.log(err)     return;   }   console.log('删除成功') }) 复制代码

7.3 fs.unlink(path, callback)

异步地删除文件或符号链接。

fs.unlink(path.join(testDir, 'abc.css'), (err) => {   if (err) {     return;   }   console.log('文件删除成功') }) 复制代码

7.4 fs.copyFile(src, dest[, mode], callback)

异步地将 src 复制到 dest

fs.copyFile(path.join(testDir, 'text.txt'), path.join(path.resolve(), 'static', 'copyText.txt'), (err) => {   if (err) {     console.log(err);     return;   }   console.log('复制成功') }) 复制代码

注意

  • 只能复制文件,不能复制目录

  • 目标目录不存在,不会创建,报错

  • 文件类型可以不一致。

8. 重命名相关

8.1 fs.rename(oldPath, newPath, callback)

将 oldPath 处的文件异步重命名为作为 newPath 提供的路径名。

fs.rename(path.join(testDir, 'newText.txt'), path.join(testDir, 'reText.txt'), (err) => {   if (err) {     console.log(err);     return;   }   console.log('重命名成功') }) 复制代码

注意

  • 不能重命名目录,只能重命名文件

  • 如果重命名文件已存在将被覆盖

10. 监听文件变化

10.1 fs.watch(filename[, options][, listener])

监视 filename 的变化,其中 filename 是文件或目录。

10.2 fs.watchFile(filename[, options], listener)

监视filename文件的变化,只能是文件

11. 文件流相关

文件流,顾名思义就是以流(stream)的形式操作文件。文件流的方式适合于对大文件的读写操作。

  • fs继承于stream

举个例子理解:

  • 想象一下,如果把文件读取比从一个水池向一个池子里抽水,同步会阻塞程序,异步会等待结果(等待池子里面的水抽完),如果这个池子特别大怎么办?有三峡水库那么大怎么办?你要等到多久才能喝到抽的水?

  • 因此便会有了文件流,文件流就好比你一边抽一边取,不用等池子满了再用一样方便。

11.1 fs.createReadStream(path[, options])

以流的形式读取文件

11.2 fs.createWriteStream(path[, options])

以流的形式写入文件

// 流的形式读取和写入,下面以复制文件的形式距离说明 const fileSource = path.join(path.resolve(), 'files/text.txt'); const pipeDest = path.join(path.resolve(), 'files/pipeCopy.txt'); // 创建可读流 const readStream = fs.createReadStream(fileSource); // 创建可写流 const writePipeStream = fs.createWriteStream(pipeDest); // pipe管道消费数据流 readStream.pipe(writePipeStream);


作者:ktb07
链接:https://juejin.cn/post/7039160563393363981


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐