[译] FileReader API

361 阅读4分钟

FildReader API - 异步读取和解析上传文件,无需后端服务器。

有没有想过,网站是如何在不存储任何数据的情况下解析上传文件的?诚然,我自己以前也有点怀疑。现在请看 FileReader API!它允许 Web 应用异步读取和解析上传的文件。

为了在自己的 Web 应用中实现这一功能,你还需要了解 File API 和 Blob API。你将使用 FileReader API 读取这些对象。你还可以使用 FileReader API 解析拖拽上传的文件。

注意:这不能与更新的 File System API 混淆,后者提供了浏览器(和客户端 JavaScript)访问本地文件系统的权限。

虽然该 API 已经存在了一段时间(Chrome 浏览器从第 6 版开始支持该 API),但现代用法中的某些部分却没有那么早得到支持,例如 File API(Chrome 浏览器 13 中提供)和 File() 构造函数(Chrome 浏览器 38 中提供)。

具有讽刺意味的是,要在本地测试此 API,必须从安全域(HTTPS)提供 JavaScript,这可以通过几款本地工具或类似 Codepen 的工具来实现。(备注:笔者从本地打开 html 文件可以 API 可以正常运行。)

基础知识

FileReader API 的基本用法是与 <input type="file" /> 结合使用。通过文件浏览器选择文件或将文件拖放到文件选择区域后,input 元素上创建会一个新的 File 对象。

FileReader 可用于任何 File 对象,但我们将探讨一个基本的使用场景。

让我们试着像这样将 File 对象传递给 FileReader API:

function readFile(file) {
    const fileReader = new FileReader();
    fileReader.readAsText(file);
}

不过,上述示例并不完整。FileReader API 默认是异步的,不过它不使用 Promise,而是监听各个事件。

现在,让我们重点关注以下内容:

function readFile() {
    const fileReader = new FileReader();
    const resultContainer = document.getElementById('result');
    const file = document.getElementById(‘uploaded-file’).files[0];

    if (file) {
        fileReader.readAsText(file);
    }
    
    fileReader.addEventListener('load', () => {
        resultContainer.innerText = fileReader.result
    }, { once: true })
}

请注意,在上面的示例中,我们将 resultContainer 的文本设置为 FileReader 触发 load 事件后得到的结果。

fileReader 对象上的 result 属性与你所期望的完全一样:FileReader 从文件中读取的任何内容都包含在 result 属性中。

虽然它不能读取所有类型的文件内容,但 FileReader 可支持以下方法:

  • readAsText()
  • readAsDataURL()
  • readAsArrayBuffer()

我将在本文中介绍前两个 API 方法,但要知道,这是一个功能强大的 API,适用于各种应用。

方法: readAsText()

首先是 FileReader API 允许我们使用的最基本的文本方法。该方法只适用于较小的文件,因为它会将整个文件读入内存。对于较大的文件,你需要使用 readAsArrayBuffer(),因为它会返回一个 promise,更适用于较大的文件的读取。

在上面的示例中,我们使用 readFile() 函数读取文件,并记录 readAsText 方法的结果。如果我们要解析任何基于文本的文件,如 .txt、.csv 等,这将非常有用。

方法: readAsDataURL()

该方法可用于读取图片,类似于将图片作为 data-url 字符串进行交互。这正是该方法的作用:以 base64 编码的 data-url 形式返回可访问的图片 url。

注意:API 是 readAsDataURL(URL 全部大写),而不是 readAsDataUrl。使用后者会导致错误,因为它区分大小写。

让我们看下面的例子:

<!-- Don't forget to write HTML semantically! -->
<label>
    <input type="file" id="uploaded-file" onchange="readImage()" name="uploaded-file" accept="image/*">
    Upload an image.
</label>
<div id="result"></div>

请注意,我使用 accept 属性将上传文件限制为图像类型的 MIME 类型。请在 MDN 查看如何指定接受的文件类型

function readImage() {
    const fileReader = new FileReader();
    const file = document.getElementById("uploaded-file").files[0];
    
    if (file) {
        fileReader.readAsDataUrl(file);
    }

    fileReader.addEventListener('load', () => {
        const result = fileReader.result;
        const resultContainer = document.getElementById("result");
        const img = document.createElement("img");
        img.src = result;
        resultContainer.append(img);
    }, { once: true })
}

在上面的例子中,我们做了以下几件事:

  1. 创建一个 FileReader 对象
  2. 监听 load 事件,load 意味着文件读取完成
  3. 创建一个 <img> 标签,并把 src 属性设置为 fileReader 返回的 data-url
  4. 最后把 <img> 标签添加到 <div> 容器中。

该 API 非常适合简单的操作,如浏览器内操作,甚至是实时状态更新,本地状态更新的同时将图片/文本/任何文件上传到后端服务器。想要在主要社交网络上更新个人资料图片?你可以使用 FileReader API 实现。

FileReader 事件

FileReader 上还有其他几个值得了解的事件。

  • abort - 文件读取已中止(取消)
  • progress - 在读取过程中定期触发
  • loadstart / loadend - 可用于在加载文件的开始或结束时触发某些操作

这些事件是从 ProgressEvent 接口扩展而来的。通过监听 FileReader 上的 progress 事件,可以计算文件读取进度,只需用 ProgressEvent 上的 loaded 属性除以 total 属性。

fileReader.addEventListener("progress", (event) => {
  const progress = (event.loaded / event.total) * 100;
  console.log(`Read progress: ${progress}%`);
});

更多阅读

FileReader 是一个出色的 API,可用于许多客户端应用。它是你可能已经使用的许多应用的基础。