登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

JavaScript FileSystemDirectoryHandle.values 怎样遍历目录条目

来源:17golang原创

时间:2026-11-02 14:53:14 219浏览 收藏

如果手里已经有一个 FileSystemDirectoryHandle,遍历目录条目最直接的写法是 for await...of 配合 values()。它返回的是异步迭代器,每次产出一个文件句柄或子目录句柄,而不是一次性生成数组。

官方文档:https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/values

实战时先处理三个边界:调用发生在合适的安全上下文中,目录句柄已经获得读取权限,遍历过程中条目可能被移动或删除。下面按这个顺序拆开。

FileSystemDirectoryHandle.values() 到底返回什么

values() 返回一个异步迭代器,迭代值是目录里的 FileSystemHandle。每个值都有 kind 和 name,其中 kind 为 file 或 directory。

FileSystemDirectoryHandle.values 返回异步迭代器并产生文件句柄或子目录句柄的结构图
图1:values() 遍历目录项的静态结构图,不是运行截图。

因此不要写成 const entries = await dirHandle.values() 后再调用数组方法。迭代器要在异步循环中消费:

async function listEntries(dirHandle) {
  // values() 逐项返回句柄,不会直接返回数组。
  for await (const handle of dirHandle.values()) {
    // name 用于展示,kind 用于决定下一步处理方式。
    console.log(handle.kind, handle.name);
  }
}

怎样区分文件和子目录

拿到句柄后先看 kind,不要仅凭名称后缀判断。文件句柄可以继续调用 getFile() 读取元数据;目录句柄则可以再次调用 values() 递归遍历。

async function readDirectFiles(dirHandle) {
  const files = [];

  // 只收集当前层文件,子目录交给调用方决定是否递归。
  for await (const handle of dirHandle.values()) {
    if (handle.kind !== "file") {
      continue;
    }

    // getFile() 是异步操作,读取失败时让上层统一处理。
    const file = await handle.getFile();
    files.push({ name: handle.name, size: file.size, type: file.type });
  }
  return files;
}

需要递归时怎样保持异步流程

递归函数最好返回异步生成器,让调用者继续用 for await...of 消费。这样不会为了遍历深层目录而一次性保存所有结果。

async function* walkFiles(dirHandle, prefix = "") {
  // 逐项处理,避免把整个目录树一次性放入内存。
  for await (const handle of dirHandle.values()) {
    const relativeName = prefix ? `${prefix}/${handle.name}` : handle.name;

    if (handle.kind === "file") {
      // 产出句柄和相对名称,读取文件内容由调用方决定。
      yield { handle, relativeName };
      continue;
    }

    // 子目录重新进入同一遍历逻辑。
    yield* walkFiles(handle, relativeName);
  }
}

async function collectNames(rootHandle) {
  const names = [];
  // 消费异步生成器,过程中的异常交给 catch 统一记录。
  for await (const item of walkFiles(rootHandle)) {
    names.push(item.relativeName);
  }
  return names;
}

权限和异常怎么处理

如果读取权限不是 granted,迭代可能抛出 NotAllowedError;条目在遍历期间消失时,可能遇到 NotFoundError。这两类错误的处理方式不同:前者提示用户重新选择或授权,后者通常记录并跳过已经消失的条目。

FileSystemDirectoryHandle.values 遍历中的权限检查、kind 分流和异常处理流程图
图2:目录遍历排障路径的静态说明图,不是运行截图。
async function safelyWalk(rootHandle) {
  try {
    // 统一消费异步迭代器,保证循环中的异常能被捕获。
    for await (const item of walkFiles(rootHandle)) {
      console.log(item.relativeName);
    }
  } catch (error) {
    // 只把已知边界转成可读提示,未知错误继续抛出。
    if (error?.name === "NotAllowedError") {
      throw new Error("目录读取权限不足,请重新选择目录并授权", { cause: error });
    }
    if (error?.name === "NotFoundError") {
      console.warn("遍历时有条目已经不存在,继续处理现有结果");
      return;
    }
    throw error;
  }
}

最后怎样判断写法是否正确

可以用下面的判断快速排查:如果代码把 values() 当作数组,问题在消费方式;如果拿到目录却调用 getFile(),问题在 kind 分流;如果偶发权限错误,问题通常在读取授权或安全上下文;如果只在目录变化时失败,则要把条目消失视为正常竞态。

归纳起来,稳定的目录遍历链路是:目录句柄 → values() 异步迭代器 → for await...of → 按 kind 分流 → 在边界处处理权限和条目变化。把这条链路拆开后,文件读取与目录递归都能保持清晰。

相关问题

values() 和 entries() 有什么区别? values() 只产出句柄;entries() 同时产出名称与句柄组成的键值对。只需要句柄时,values() 更直接。

为什么不能用普通 for...of? 因为目录迭代器是异步的,需要 for await...of 等待每一项。

如何只读取当前层? 不对 kind === "directory" 递归,只处理当前循环中 kind === "file" 的句柄。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>