07 文件权限与常见错误

1. Linux 权限基础

Linux 权限分为所有者、组和其他用户,每类包含:

r 读
w 写
x 执行;对目录表示进入/遍历

读取一个文件不仅需要文件可读,还需要对父目录具有遍历权限。创建或删除文件通常取决于父目录写权限,而不只是目标文件自身权限。

PM2 以哪个 Linux 用户启动,Node.js 就通常以哪个用户访问文件。Nginx 用户不应直接拥有全部业务存储写权限;公开静态目录和私有上传目录要分开。

2. Windows 权限

Windows 主要使用 ACL。Node.js 的 mode 参数在 Windows 上不能机械套用 Linux 权限语义;文件被杀毒软件、编辑器或其他进程占用时,还可能得到 EPERMEBUSY

3. 识别 NodeJS.ErrnoException

export function isErrnoException(
  error: unknown,
): error is NodeJS.ErrnoException {
  return error instanceof Error && "code" in error;
}

使用:

try {
  await readFile(filePath);
} catch (error: unknown) {
  if (isErrnoException(error)) {
    switch (error.code) {
      case "ENOENT":
        throw new AppFileError("FILE_NOT_FOUND", error);
      case "EACCES":
      case "EPERM":
        throw new AppFileError("FILE_ACCESS_DENIED", error);
    }
  }

  throw error;
}

不要通过 error.message.includes("no such file") 判断;不同操作系统和 Node.js 版本的文本可能变化。

4. 高频错误码

code 常见原因 处理方向
ENOENT 文件或某级父目录不存在 检查路径和生命周期
EACCES 权限不足 检查运行用户和目录权限
EPERM 操作不允许/Windows 占用 检查 ACL、占用和操作类型
EEXIST 排他创建目标已存在 生成新名称或返回冲突
EISDIR 把目录当文件 检查 Entry 类型
ENOTDIR 路径中某段不是目录 检查路径冲突
ENOTEMPTY 删除非空目录 确认是否允许递归删除
EMFILE 进程打开句柄过多 限制并发、关闭资源
ENOSPC 磁盘空间或 inode 耗尽 停止任务、告警、清理
EROFS 只读文件系统 修复挂载/写入位置
EXDEV 跨设备 rename 复制成功后再删除
EBUSY 资源占用 有界重试或等待占用释放

5. 重试不是万能解法

EBUSY、某些 Windows EPERM 可能短暂出现,可以少量、有退避地重试。EACCESENOENT、路径穿越不会因为盲目重试而恢复。重试前先判断错误是否暂时性,并设置最大次数。

6. 公开错误与日志

响应:

{
  "code": "FILE_PROCESSING_FAILED",
  "message": "文件处理失败",
  "data": null
}

日志可以包含:

taskId、stage、relativePath、errno、syscall

不要把患者文件名、完整绝对路径和文件内容无条件返回前端。

7. umask

创建文件时最终权限会受到进程 umask 影响。不要只看到源码中的 mode: 0o666 就断言磁盘权限一定如此。生产部署应检查实际生成文件,并确保私有上传内容不被其他系统用户随意读取。

练习题

  1. 分别制造 ENOENTEEXISTEISDIR
  2. 编写错误映射器,保留原始错误作为 cause
  3. 在 Ubuntu 上解释目录 rwx 对创建、列出和进入的影响。
  4. 为什么 ENOSPC 后继续重试可能让系统更糟?