Back

使用 FileReader API 处理文件

使用 FileReader API 处理文件

当用户在你的 Web 应用中选择文件时,你会得到一个 File 对象——但如何实际读取其内容呢?这就是 JavaScript FileReader API 的用武之地。本文将介绍 FileReader 的工作原理、使用场景,以及它与现代替代方案的比较。

核心要点

  • FileReader API 使用三种方法之一异步读取 FileBlob 内容:readAsText()readAsArrayBuffer()readAsDataURL()
  • FileReader 使用事件驱动模型,通过 loaderrorprogress 等事件来传递结果。
  • 你可以将 FileReader 封装在 Promise 中,以便更好地与 async/await 代码集成。
  • 现代的 Blob.text()Blob.arrayBuffer() 方法对于基本读取更简单,但当你需要进度跟踪、非 UTF-8 编码或 base64 数据 URL 时,FileReader 仍然是正确的选择。

什么是 FileReader API?

FileReader API 是一个浏览器接口,用于异步读取 FileBlob 对象的内容。它不能通过路径访问用户系统上的任意文件——它只能处理用户明确选择的文件,无论是通过 <input type="file"> 还是拖放交互。

const input = document.querySelector('input[type="file"]')

input.addEventListener('change', () => {
  const file = input.files[0]
  console.log(file.name, file.size, file.type)
})

File 对象继承自 Blob,因此它具有相同的属性,外加 namelastModified。你可以直接将它传递给任何 FileReader 读取方法。

三种核心读取方法

在 JavaScript 中使用 FileReader 读取本地文件意味着选择正确的输出格式:

  • readAsText(file, encoding?) — 将文件内容作为字符串返回。默认为 UTF-8。适用于 CSV、JSON、纯文本或任何基于文本的格式。
  • readAsArrayBuffer(file) — 将原始二进制数据作为 ArrayBuffer 返回。用于图像、音频、PDF 或任何二进制处理。
  • readAsDataURL(file) — 返回 base64 编码的 data: URL。便于图像预览,但请注意,由于 base64 编码开销,结果大约比原始文件大 33%。

避免使用 readAsBinaryString() — 它已被弃用。对于二进制数据,请改用 readAsArrayBuffer()

FileReader 的事件驱动模型

FileReader 是异步的,通过事件进行通信。关键事件包括:

事件触发时机
loadstart开始读取
progress读取期间定期触发
load读取成功完成
error读取失败
loadend读取结束(成功或失败)

以下是使用 readAsDataURL() 在上传前预览图像的实际示例:

function previewImage(file) {
  const reader = new FileReader()

  reader.addEventListener('load', () => {
    const img = document.querySelector('#preview')
    img.src = reader.result
  })

  reader.addEventListener('error', () => {
    console.error('Failed to read file:', reader.error)
  })

  reader.readAsDataURL(file)
}

progress 事件对于显示大文件的进度条特别有用:

reader.addEventListener('progress', (event) => {
  if (event.lengthComputable) {
    const percent = Math.round((event.loaded / event.total) * 100)
    progressBar.value = percent
  }
})

将 FileReader 封装在 Promise 中

FileReader 的回调风格在现代异步代码中可能显得笨拙。一个简单的封装可以解决这个问题:

function readFileAsText(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.addEventListener('load', () => resolve(reader.result))
    reader.addEventListener('error', () => reject(reader.error))
    reader.readAsText(file)
  })
}

// 使用方式
const text = await readFileAsText(file)

FileReader 与 Blob.text() 和 Blob.arrayBuffer() 的对比

现代浏览器直接在 Blob(因此也包括 File)上支持基于 Promise 的方法:

// 现代方法 — 对于基本读取更简单
const text = await file.text()
const buffer = await file.arrayBuffer()

那么什么时候应该继续使用 FileReader?在以下情况下选择它:

  • 需要通过 progress 事件进行进度跟踪(尽管对于本地文件,某些浏览器可能只触发少量进度事件)
  • 需要通过 readAsText(file, 'ISO-8859-1') 支持非 UTF-8 编码
  • 需要通过 readAsDataURL() 获取 Base64 数据 URL
  • 需要更广泛的旧版浏览器支持

对于现代应用中的简单文本或二进制读取,Blob.text()Blob.arrayBuffer() 更简洁。对于图像预览,URL.createObjectURL(file) 避免了 base64 膨胀,通常比 readAsDataURL() 更节省内存。只需记住在不再需要对象 URL 时调用 URL.revokeObjectURL() 以避免内存泄漏。

结论

FileReader API 仍然是在浏览器中处理 File 对象的实用工具——特别是当你需要进度事件、编码控制或数据 URL 时。对于更简单的情况,较新的 Blob 方法需要更少的代码。掌握两者,你就能干净利落地处理任何文件读取场景。

常见问题

不能。FileReader 只能读取用户通过 input 元素或拖放交互明确选择的文件。浏览器的安全模型阻止 JavaScript 通过路径访问文件系统上的任意文件。这一限制的存在是为了保护用户隐私和系统安全。

readAsArrayBuffer 返回适合处理或操作的原始二进制数据,例如解析图像元数据或音频波形。readAsDataURL 返回一个 base64 编码的字符串,可以直接用作图像 src 或 CSS 背景。数据 URL 方法很方便,但产生的输出大约比原始文件大 33%。

如果你只需要文本内容并且你的应用面向现代浏览器,Blob.text() 更简单,因为它直接返回 Promise。当你需要为大文件进行进度跟踪、需要指定非 UTF-8 编码,或需要支持缺少 Blob.text() 方法的旧版浏览器时,请使用 FileReader。

如果你使用 URL.createObjectURL 生成预览 URL,请在图像加载完成或预览被移除后调用 URL.revokeObjectURL。如果你改用 readAsDataURL,base64 字符串会由垃圾回收自动管理,但对于相同的文件,它比对象 URL 消耗更多内存。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay