专栏名称: 前端早读课
我们关注前端,产品体验设计,更关注前端同行的成长。 每天清晨五点早读,四万+同行相伴成长。
目录
相关文章推荐
前端早读课  ·  【第3470期】利用大型语言模型(LLMs) ... ·  昨天  
CEO品牌观察  ·  听小野主理人 讲述小野全球首店里的故事 ·  2 天前  
CEO品牌观察  ·  听小野主理人 讲述小野全球首店里的故事 ·  2 天前  
前端大全  ·  被骂了!腾讯道歉 + 立刻改正 ·  4 天前  
前端大全  ·  告别 axios,ngify 让你的 ... ·  3 天前  
吉林省高级人民法院  ·  “只要是前往正义的路我都想走” ·  3 天前  
吉林省高级人民法院  ·  “只要是前往正义的路我都想走” ·  3 天前  
51好读  ›  专栏  ›  前端早读课

【第3450期】JavaScript 中的千里眼和穿杨箭

前端早读课  · 公众号  · 前端  · 2025-01-24 08:00

正文

前言

提前祝大家春节快乐,节前就分享到今天了,2.5见。介绍了 JavaScript 中的文件系统访问 API(File System Access API),包括其定义、应用场景、优点,以及通过 Demo 讲解了核心概念和功能设计,如 Picker 和 Handle 的作用及使用方法,并提到了使用中的注意事项和未来的发展方向。今日前端早读课文章由 @Jax 授权分享。

正文从这开始~~

大家好,我是 Jax。

我做 Web 开发也有七年了,在写代码的过程中收获了非常多的乐趣,所以我一直坚持在社区中做分享,把知识和快乐传递给更多的人。

那今日份的快乐来自于 File System Access API,中文意思就是文件系统访问 API。

它是 JavaScript 中一套非常硬核的组合技,就好像千里眼和穿杨箭那样神奇。

为了方便表述,我们后面都简称为文件系统 API。

那我的分享会分成四个小节。

首先跟大家讲解到底什么是文件系统 API;

然后会结合实操代码,来了解这套 API 的核心概念和功能设计。

第三小节会介绍怎么更好地使用这套 API。

最后一小节我埋了一个小彩蛋,介绍一个神秘客串嘉宾。

如果只能用一句话来定义这套 API 是什么,那就是,它是 JavaScript 提供的一系列属性和方法,让我们能够访问到用户本地设备的文件和文件夹。

这么概括还是有点笼统。那我们除了要说什么是,还要说说说什么不是。

这样可以排除一些容易混淆的概念,加深理解。

文件 input 标签大家一定很熟悉,做文件上传组件总是少不了它,但它不属于这套 API 的范畴。

说到 input 标签,这里插播一条脑筋急转弯,说有一个父文件夹,里面嵌套了不知道多少层子文件夹,每个子文件夹里也不知道有多少文件。问,如何用最少的代码 —— 注意是最少的代码 —— 计算出这个文件夹中一共有多少文件?

大家有思路吗?那我先给大家炫儿一个,请看屏幕右侧。

我们先用一个文件 input 标签,与以往不同的是,我们给它加上一个 webkitdirectory 特性,这样就能上传整个文件夹了。

上传文件夹后,所有的文件就被自动提取到 files 数组中了。

这样,三行代码就能得到文件总数。大家回头可以试一试这个小窍门。

好我们回到正题,在 JavaScript 中,还有另外一套 API,叫「文件和目录入口」API,尽管名字和功能都很像,但确实不属于我们今天要聊的话题。

那在我看来,这套 API 整体上可以分为一前一后两个部分,前面的是 picker,用来提供用户界面来上传文件;后面的是 Handle,用于管理和操作所上传的文件。

所以,有了 Picker 和 Handle,凡是涉及到文件上传和处理的地方,都可以用上这套 API。

比如在线的文本编辑器、图片处理工具,以及视频剪辑应用等等。

我们来看一个真实案例。屏幕左侧这个界面是 Photopea,一个在线 P 图工具,类似 Photoshop。它支持用户上传本地文件,然后对图片进行编辑。

屏幕右侧的截图,是我在 Photopea 页面扒出来的和上传文件相关的代码逻辑。仔细看的话,能发现这里用的就是文件系统访问 API。

这套 API 不仅能用,而且非常好用。

它让 Web 应用能够穿透浏览器容器,触碰到用户本地的文件;而且带来了极强的文件操控能力,这就是为什么我把它比喻成千里眼和穿杨箭。

此外,它能与 IndexedDB、Web Worker 一起配合使用,具备持久化和并行能力。能带来原生级别的体验。

而且由于文件直接在前端代码里处理,无需上传云端,极大地保证了私密性,同时也为用户和我们自己节省了服务成本。

OK 以上和大家介绍了这套 API 的定义、应用场景和优点,接下来,我们结合一个 Demo,来深入了解一下这套 API 的核心概念和功能设计。

我们先来梳理一下核心概念。刚才我们提到,整体来看,这套 API 分为两个部分,前面是 Picker,后面是 Handle。

那么 PIcker 其实就是我们日常选择文件的系统弹窗,不同的操作系统,其 Picker 样式也不同。屏幕上这个是 MacOS 的 Picker 截图。

JavaScript 在 window 对象上提供了方法,让我们可以直接唤起 Picker 界面。我们首先来看选择文件的 Picker。

这个方法接收一些可选参数,我们简要介绍一下。

id 和 startsIn 都是用来指定快捷路径,帮用户提高操作效率。

multiple 用来控制是否多选。

这个名字老长的 excludeAcceptAllOption,简单理解就是否是限制文件类型,true 就是限制,false 就是不限制。如果为 true,那么就必须要设置下面这个 types 属性。我们可以把允许用户选择的文件类型放进去。

第二个 picker 是用来选择文件夹的 Picker,除了刚才讲的 id 和 startsIn,它还有一个 mode 参数,用来控制读写权限。

第三个 Picker 是用来选择保存文件的,我们可以向用户建议保存位置、类型,以及文件名。

OK,当用户通过 Picker 上传了文件之后,picker 方法会返回一个 Promise,这个 Promise fulfill 出来的对象,就是 API 的另一个核心概念:Handle。

对于这位憨豆先生,API 是这么设计的:顶层是一个基类 FileSystemHandle,具有一些公用的属性和方法;它下面有两个子类,分别对应文件和文件夹类型的 Handle。在日常使用过程中,我们都是使用子类实例,很少直接操作基类。

Handle 是对文件和文件夹入口的抽象,所以具有 name 和 kind 属性,name 对应文件或文件夹名;kind 的值可以是 file 或者 directory,可以帮我们区分类型。

同时它还有一些方法可以调用,比如判断两个实例是否指向同一个入口、移除入口等等。

而对于文件类型的 Handle,它支持对文件的读写,所以我们可以用 getFile 来拿到文件实体,以及用 createWritable 写入数据。

文件夹 Handle 在方法设计上会有很大不同,因为文件夹可能包含子文件和子文件夹,所以它支持在当前文件夹 Handle 上调用方法去获取子 Handle,也支持对子 Handle 做移除、重命名等操作。

好了,铺垫完了核心概念,我们来看一个具体的例子。这是我写的一个 Demo,这是一个微型的文件管理系统,用户可以选择一个文件夹来载入,然后就可以在 Web 页面上对文件夹内容进行管理了。支持的操作包括对文件和文件夹的增删改查,能够比较全面地体现这套 API 的强大能力。

接下来,我会带着大家逐一拆解这个 Demo 的核心功能。这样听我讲完一遍,大家也就基本能掌握这套 API 的用法了。

我们先来看怎么载入一个文件夹。这里我们要实现的是让用户选一个文件夹,我们来读取文件夹的下一级子内容,当然,你也可以按照深度或者广度遍历,把每一层都读取到。这里我们为了保持简洁,就只读下一层。

我们执行 showDirectoryPicker 拿到文件夹 Handle,再遍历它的子内容,从子内容的属性上可以拿到对应的名称和类型。

接着我们来看如何读取文件内容。

这里有两种方式,一种是直接让用户载入一个文件,另一种是已经载入了文件夹,从文件夹找子文件。

拿到文件 Handle 之后,通过 getFile 和 text 方法,就能读取到文件内容

那么如何新建文件和文件夹呢?

这里前提是我们已经拿到了根文件夹或者说父文件夹的 Handle,然后在父文件夹里新建。

我们用 Prompt 方法让用户输入名称,然后在父 Handle 上调用 getXXXHandle。这两个方法,如果只传 name,就是按名称查找子 Handle,而如果额外传了 create true,就代表新建了。

现在我们来实现重命名功能,同样是用 Prompt 获取用户输入值,用做新的名称,然后在文件 Handle 上调用 move 方法,就把文件名改了。

目前我还没找到重命名文件夹的方法,不过相信以后会有的。

接着我们来看编辑文件内容。我们知道,这实际上是在原有内容的基础上进行编辑,所以我们还是先执行读取文件内容的逻辑,拿到原来的文件内容。

在实现编辑界面上,你可以为文本内容配备上富文本编辑器,或者为图片配备图片编辑组件。这里为了最简化,我们以文本为例,把原有内容作为第二个参数传入 Prompt,这样,在浏览器弹出的输入框中,就会预填文件内容,用户就可以直接在里面编辑了。

用户编辑后,我们调用 createWritable,把编辑后的文本写入到原来的文件 Handle 中,这样,编辑文件功能就实现了。

最后我们来做删除功能。无论是文件还是文件夹,我们都可以在 Handle 上调用 remove 方法。区别在于,由于文件夹可能含有多级子内容,所以在删除时,需要传入 recursive: true。







请到「今天看啥」查看全文