nodejs 之 fs-文件系统

1,176 阅读5分钟

引入模块

const fs = require('fs');

同步和异步

所有文件系统的操作都具有同步和异步的形式

  • 异步的形式总是将完成回调作为其最后一个参数。 传给完成回调的参数取决于具体方法,但第一个参数始终预留用于异常。 如果操作成功完成,则第一个参数将为 null 或 undefined。
    删除
const fs = require('fs');
fs.unlink('/tmp/hello', (err) => {
  if (err) throw err;
  console.log('已成功删除 /tmp/hello');
});
  • 使用同步的操作发生的异常会立即抛出,可以使用 try…catch 处理,也可以允许冒泡。
const fs = require('fs');
try {
  fs.unlinkSync('/tmp/hello');
  console.log('已成功删除 /tmp/hello');
} catch (err) {
  // 处理错误
}
  • 使用异步的方法时无法保证顺序。 因此,以下的操作容易出错,因为 fs.stat() 操作可能在 fs.rename() 操作之前完成:
    fs.stat() 查看文件状态
    fs.rename() 重命名
fs.rename('/tmp/hello', '/tmp/world', (err) => {
  if (err) throw err;
  console.log('重命名完成');
});
fs.stat('/tmp/world', (err, stats) => {
  if (err) throw err;
  console.log(`文件属性: ${JSON.stringify(stats)}`);
});

正确排序,将fs.stat调用移动到fs.rename操作的回调中:

fs.rename('/tmp/hello', '/tmp/world', (err) => {
  if (err) throw err;
  fs.stat('/tmp/world', (err, stats) => {
    if (err) throw err;
    console.log(`文件属性: ${JSON.stringify(stats)}`);
  });
});

建议使用异步进行操作,同步版本将阻塞整个进程,直到他们完成(停止所有连接)

文件路径

  • fs.open(path, flag[, mode], callback)
    • path string | Buffer | URL
    • flags string | number
    • mode integer Default: 0o666
    • callback Function
      • err
      • fd 文件描述符
  • 异步地打开文件。 flags 可以是:
    • ‘r’ --- 以读取模式打开文件。如果文件不存在则发生异常。
    • ‘r+’ --- 以读写模式打开文件。如果文件不存在则发生异常。
    • ‘rs+’ --- 以同步读写模式打开文件。命令操作系统绕过本地文件系统缓存。
      这对 NFS 挂载模式下打开文件很有用,因为它可以让你跳过潜在的旧本地缓存。 它对 I/O 的性能有明显的影响,所以除非需要,否则不要使用此标志。
‘w’ - 以写入模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
‘wx’ - 类似 ‘w’,但如果 path 存在,则失败。
‘w+’ - 以读写模式打开文件。文件会被创建(如果文件不存在)或截断(如果文件存在)。
‘wx+’ - 类似 ‘w+’,但如果 path 存在,则失败。
‘a’ - 以追加模式打开文件。如果文件不存在,则会被创建。
‘ax’ - 类似于 ‘a’,但如果 path 存在,则失败。
‘a+’ - 以读取和追加模式打开文件。如果文件不存在,则会被创建。
‘ax+’ - 类似于 ‘a+’,但如果 path 存在,则失败。

fs.open() 同步版本--- fs.openSync() 进入同步阻塞调用。

fs.open('/open/some/file.txt', 'r', (err, fd) => {
  if (err) throw err;
  fs.fstat(fd, (err, stat) => {
    if (err) throw err;
    // 使用文件属性。
    // 始终关闭文件描述符!
    fs.close(fd, (err) => {
      if (err) throw err;
    });
  });
});

fs 的 api

- fs.Dir

async function print(path) {
  const dir = await fs.promises.opendir(path);
  for await (const dirent of dir) {
    console.log(dirent.name);
  }
}
print('./test').catch(console.error);

遍历文件目录
- dir.close(callback) ---异步

大多数操作系统限制在任何给定时间内可能打开的文件描述符的数量,因此当操作完成时关闭描述符至关重要。 如果不这样做将导致内存泄漏,最终导致应用程序崩溃。

- dir.closeSync()   ---同步
- dir.path   ---此目录的只读路径
- dir.read(callback)   ---

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

  • path | |
  • mode 默认值: fs.constants.F_OK。
  • callback
    • err 测试用户对 path 指定的文件或目录的权限。

文件可访问性的常量

常量 说明
F_OK 表明文件对调用进程可见。 这对于判断文件是否存在很有用,但对 rwx 权限没有任何说明。 如果未指定模式,则默认值为该值。
R_OK 表明调用进程可以读取文件。
W_OK 表明调用进程可以写入文件。
X_OK 表明调用进程可以执行文件。 在 Windows 上无效(表现得像 fs.constants.F_OK)。

如:

const file = 'package.json';
// 检查当前目录中是否存在该文件。
fs.access(file, fs.constants.F_OK, (err) => {
  console.log(`${file} ${err ? '不存在' : '存在'}`);
});

不建议在调用 fs.open()fs.readFile()fs.writeFile()等直接操作文件的api之前使用 fs.access()检查文件的可访问性。 这样做会引入竞态条件,因为其他进程可能会在两个调用之间更改文件的状态。 相反,应该直接打开、读取或写入文件,如果文件无法访问则处理引发的错误。

fs.existsSync(path)

如果路径存在,则返回 true,否则返回 false。

if (fs.existsSync('/etc/passwd')) {
  console.log('文件已存在');
}
const fs = require('fs');
const assert = require('assert');

console.log(fs.readFileSync('./test/test1.txt', 'utf8'));
// 打印: Node.js

// 获取要截断的文件的文件描述符。
const fd = fs.openSync('./test/test1.txt', 'r+');

// 将文件截断为前 4 个字节。
fs.ftruncate(fd, 4, (err) => {
  assert.ifError(err);
  console.log(fs.readFileSync('./test/test1.txt', 'utf8'));
});
// 打印: Node
  1. fs.mkdir(path[, options], callback)
    recursive 默认值: false,指示是否应创建父文件夹
    mode Windows 上不支持。默认值: 0o777。用于设置文件模式(权限和粘滞位),但仅限于创建文件时。
// 创建 /tmp/a/apple 目录,无论是否存在 /tmp 和 /tmp/a 目录。
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});
  1. fs.opendir(path[, options], callback) 异步地打开目录

  2. fs.read(fd, buffer, offset, length, position, callback)
    从 fd 指定的文件中读取数据。
    buffer 是数据将写入的缓冲区。
    offset 是 buffer 中开始写入的偏移量。
    length 是一个整数,指定要读取的字节数。
    position 参数指定从文件中开始读取的位置。 如果 position 为 null,则从当前文件位置读取数据,并更新文件位置。 如果 position 是整数,则文件位置将保持不变。
    回调有三个参数 (err, bytesRead, buffer)。
    如果调用此方法的 util.promisify() 版本,则返回的 Promise 会返回具有 bytesRead 属性和 buffer 属性的 Object。

  3. fs.readdir(path[, options], callback)
    读取目录的内容
    回调有两个参数 (err, files),其中 files 是目录中的文件名的数组(不包括 '.' 和 '..')。

  4. fs.readFile(path[, options], callback)
    异步地读取文件的全部内容。、

fs.readFile('./test/test1.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

fs.readFile() 函数会缓冲整个文件。 为了最小化内存成本,尽可能通过 fs.createReadStream() 进行流式传输。 6. fs.rename(oldPath, newPath, callback)

fs.rename('旧文件.txt', '新文件.txt', (err) => {
  if (err) throw err;
  console.log('重命名完成');
});
  1. fs.unlink(path, callback)
    删除文件或符号链接。
fs.unlink('path/file.txt', (err) => {
  if (err) throw err;
  console.log('文件已删除');
});
  1. fs.rmdir()
    删除文件夹

参考文档:
nodejs官网
POSIX介绍
fs模块-fs.open