当前位置:首页 > 文章列表 > 文章 > 前端 > JavaScript FileSystemDirectoryHandle.values 怎样遍历目录条目

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

来源:17golang原创 2026-11-02 14:53:14 0浏览 收藏

如果手里已经有一个 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删除
JavaScript File System Access API 怎样从文件句柄创建可写流JavaScript File System Access API 怎样从文件句柄创建可写流
上一篇
JavaScript File System Access API 怎样从文件句柄创建可写流
JavaScript Promise.withResolvers 怎样拆开 Promise 控制器
下一篇
JavaScript Promise.withResolvers 怎样拆开 Promise 控制器
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    845次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    845次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    928次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    900次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    730次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码