标签: file api

  • 使用 File System Access API 在浏览器里操作本地文件

    使用 File System Access API 在浏览器里操作本地文件

    如《Webpack 5 发布,Chrome 86 开始支持本地文件系统》一文所述,Chrome 86 开始,浏览器正式支持操作本地文件。接下来结合最近的使用,分享下用法。

    0. 准备工作:理清概念

    首先,我们要先搞清楚一些概念。实际上,让浏览器操作本地文件是开发者一直在努力并且不停在探索的方向,所以历史上有很多方案,存在很多名称接近但其实并不一样的 API,大家在学习的时候一定要搞清楚,不要弄混。

    最早登场的是 File API,代表功能是 FileReader(参考:《使用 Promise 封装 FileReader》)。这个 API 最大的进步在于,我们可以在浏览器里读取文件,然后操作二进制数据;最后还可以构建内存 URL,并通过 <a download="file.{ext}"> 下载到本地。如此,浏览器可以操作几乎所有二进制数据,作为工具平台的价值大大提高。

    接下来,激进的 Google Chrome 提出并实现了 File System API。这个 API 试图在浏览器里创建一个独立的文件环境,让开发者可以在里面任意操作文件和目录。如果能做好,这会是一个非常好的实现,可以大大提升浏览器作为系统核心的地位。可惜步子不仅大、而且偏,最终失败。我总结原因有二:

    • 一方面,“独立的文件环境”,即无法操作系统本地文件,其实没什么价值,反而增加了用户理解和使用时的心智负担。
    • 另外,当时浏览器的其它限制没有突破——没有包管理、没有 babel、甚至没有 Promise,IE 仍然大量存在,开发难度极大。

    所以最终这套方案死得悄无声息。我曾经的博客《HTML5的File API应用》,可能是为数不多的中文分享。

    接下来是 Chrome Extension、Chrome App、Chrome OS 里的 File (System) API。这几个产品都是 Google 私有,不用考虑其他浏览器厂商,所以可以放开手脚随便搞。这里大家需要注意的是,因为 Google 的产品策略一向是说关就关,所以大家要留心常看文档,别学了一半 API 没了,比如:Extension 的 chrome.fileSystem 就已经弃用了

    最后,也就是今天的主角,File System Access API。这套方案应该是未来的主角。它提供了比较稳妥的本地文件交互模式,即保证了实用价值,又保障了用户的数据安全,明显是前辈 File System API 的继任者。

    它的设计思路也不复杂:

    1. 要求用户手动选择文件或者目录,以获取文件或目录的控制权限
    2. 选择文件或目录后,获取到 FileHandle,后续的操作经由它来进行
    3. FileHandleserializable 对象,所以可以通过序列化和反序列化实现跨 session 的存储(即刷新页面后还能用)

    好,下面看代码。

    1. 读取本地文件

    这段代码可以比较完整的演示 window.showOpenFilePicker API 的用法:

    // 使用 `try...catch` 可以捕获用户取消选择时抛出的错误,如果你对错误不在意,不捕获也行
    try {
      const [handle] = await showOpenFilePicker({
        multiple: false, // 只选择一个文件
        types: [
          {
            description: 'Navlang Files',
            accept: {
              'text/x-navlang': '.nav',
            },
          },
        ],
        excludeAcceptAllOption: true,
      });
    } catch (e) {
      if (e.message.indexOf('The user aborted a request') === -1) {
        console.error(e);
        return;
      }
    }
    
    // 如果没有选择文件,就不需要继续执行了
    if (!handle) {
      return;
    }
    
    // 这里的 options 用来声明对文件的权限,能否写入
    const options = {
      writable: true,
      mode: 'readwrite',
    };
    // 然后向用户要求权限
    if ((await handle.queryPermission(options)) !== 'granted'
      && (await handle.requestPermission(options)) !== 'granted') {
      alert('Please grant permissions to read & write this file.');
      return;
    }
    
    // 前面获取的是 FileHandle,需要转换 File 才能用
    const file = await handle.getFile();
    // 接下来,`file` 就是普通 File 实例,你想怎么处理都可以,比如,获取文本内容
    const code = await file.text();

    2. 保存本地文件

    前面说过,FileHandle 可以序列化,也即可以进行持久化存储。所以我们只需要把对应的 FileHandle 存下来,然后保存即可。

    if (data.file) {
      const writable = await data.file.createWritable();
      await writable.write(data.code);
      await writable.close();
    }

    如果之前没有获取过 FileHandle,则可以通过 window.showSaveFilePicker 来获取:

    try {
      const file = await showSaveFilePicker(filePickerOptions);
    } catch (e) {
      if (e.message.indexOf('The user aborted a request.') === -1) {
        console.error(e);
      }
      return;
    }
    // 然后接前面的代码
    const writable = await file.createWritable();
    await writable.write(data.code);
    await writable.close();

    这个功能现在有一点小问题,不知道是不是 Chrome 实现不太稳定,如果你打开开发者工具,然后钩上“Pause on caught exceptions”,那么保存时会暂停数次,并提示错误。不用理会,直接继续执行即可。我猜测这个过程本来应该由浏览器自动捕获并重试,直到超时保护或者写入成功,但是现在会错误地抛出来。

    3. 总结

    File System Access API 不仅可以操作文件,还可以操作目录,操作目录的方式和文件相仿,我就不详细举例了,大家可以看下后面的参考链接,或者等我用到目录、踩了坑再来分享。

    这个 API 对前端来说意义不小。有了这个功能,Web 可以提供更完整的功能链路,从打开、到编辑、到保存,一套到底。虽然目前只有 Chrome 支持,但还是建议大家尽快把它用起来。


    参考链接:

  • HTML5的File API应用

    HTML5的File API应用

    HTML5新增了很多特性,其中File API是非常重要的部分。在肉大师中,我大量使用了HTML5的文件API,这样一来可以给予用户近乎桌面软件的体验,二来还能减少服务器和带宽的消耗。今天终于把最后几个问题解决了,在这里总结下HTML5 File API的使用。

    随着用的越来越多,发现自己其实搞混了“File API和FileSystem API”两个东西。而且类写的也有问题。等到有空的时候把这篇文章重写一下好了。(2012-09-13)

    用途

    在W3C页面上,列出了File API可能用到的场合(以下为意译,可能有所偏颇,欢迎对比原文阅读):

    1. 断点续传
      • 上传时,先把目标文件复制到本地沙箱,然后分解逐块上传
      • 浏览器崩溃或者网络中断也没关系,因为恢复后可以续传
    2. 需要大量媒体素材的应用,比如视频游戏
      • 下载压缩包,在本地解压,就能恢复之前目录结构
      • 跨平台
      • 通过渐进式下载,进入新关卡或者开启新功能均无需等待,因为玩的时候所需素材已经通过后台下载完成了
      • 从本地缓存中直接读取素材,速度飞快
      • 二进制文件也不在话下
      • 使用压缩包可以大大减轻带宽和服务器消耗,也避免了频繁下载碎片文件带来的检索问题
    3. 离线图片/音频编辑器通
      • 不怕频繁读写大量数据
      • 只想重写文件的某些部分也能做到(比如修改ID3或者EXIF信息)
      • 创建目录组织项目后用起来舒服多了
      • 编辑完的文件还能被iTunes、Picasa之类的本地应用访问
    4. 离线视频播放器
      • 下载超过1G的大文件,将来想看再看
      • 可以在不同时间点间来回跳转播放
      • 能够给Video标签提供URL
      • 即便片子还没下完,也能把下载到的部分先睹为快
      • 还能任意截取一段视频交给Video标签播放
    5. 离线邮件客户端
      • 下载保存附件到本地自不必说
      • 断网的情况下,可以缓存用户要上传的附件,以后再上传
      • 需要时可以列出缓存里的附件,通过缩略图显示,预览后上传
      • 能像正常服务器那样触发标准的下载动作
      • 不仅能使用XHR一次性上传全部内容,还可以把邮件和附件拆解成小块依次发送

    听起来都是些令人振奋的功能,实际用起来还是要踩点坑。下面就把我的经验分享一下。

    (更多…)