FileAPI与BlobAPI

2023/5/9

# FileAPI 与 BlobAPI

# File 类型

File API 提供了直接访问文件信息的能力。HTML5 在 DOM 上为文件输入元素添加了 files 集合。当用户在文件字段中选择一个或多个文件时,这个 files 集合中会包含一组 file 对象,表示被选中的文件。每个 FIle 对象都有一些只读属性。

  • name:文件名
  • size:文件大小(字节)
  • type:文件 MIME 类型
  • lastModifiedDate:文件最后修改时间

可以通过监听 change 事件然后遍历 files 集合可以取得每个选中文件的信息:

let filesList = document.getElementById('file-list')
filesList.addEventListener('change', (event) => {
    let files = event.target.files
    let i = 0
    let len = files.length
    while (i < len) {
        const f = files[i]
        console.log(`${f.name} (${f.type}, ${f.size}bytes)`)
        i++
    }
})
1
2
3
4
5
6
7
8
9
10
11

# FileReader 类型

FileReader 类型表示一种 异步 文件读取机制,用于从文件系统中读取文件。FileReader 类型提供了几个读取文件数据的方法。

  • readAsText(file, encoding):从文件中读取纯文本内容并保存在 result 属性中。
  • readAsDataURL(file):读取文件并将内容的数据 URI 保存在 result 属性中。
  • readAsBinaryString(file):读取文件并将每个字符的二进制数据保存在 result 属性中。
  • readAsArrayBuffer(file):读取文件并将文件内容以 ArrayBuffer 形式保存在 result 属性中。

这些读取数据的方法为处理文件数据提供了极大的灵活性。例如,为了向用户显示图片,可以将图片读取为数据 URI,而为了解析文件内容,可以将文件读取为文本。

因为读取方法是异步的,所以每个 FileReader 会发布几个事件,其中 3 个最有用的事件是 progress、error 和 load,分别表示还有更多数据、发生了错误和读取完成。

progress 事件梅 50 毫秒就会触发一次,包含三个信息:lengthComputable、loaded 和 total。此外,在 progress 事件中可以读取 fileReader 的 result 属性,即使其中尚未包含全部数据。

error 事件会在由于某种原因无法读取文件时触发。触发 error 事件时,FileReader 的 error 属性会包含错误信息。这个属性是一个对象,只包含一个属性:code。这个错误码的值可能是 1 (未找到文件)、2(安全错误)、3(读取被中断)、4(文件不可读)或 5(编码错误)。

load 事件会在文件成功加载后触发。如果 error 事件被触发,则不会触发 load 事件。下面的例子演示了所有这 3 个事件。

const files = event.target.files // 根据文件字段获取到的文件集合
let reader = new FileReader()
let type = 'default'

if (/image/.test(files[0].type)) {
    reader.readAsDataURL(files[0])
    type = 'image'
} else {
    reader.readAsText(files[0])
    type = 'text'
}
reader.onerror = function(error) {
    console.log(`Could not read file, error code is ${reader.error.code}`)
}
reader.onprogress = function(event) {
    if (event.lengthComputable) {
        console.log(`${event.loaded}/${event.total}`)
    }
}
reader.onload = function() {
    let html = ''
    switch(type) {
        case 'image':
            html = `<img src="${reader.result}">`
            break
        case 'text':
            html = reader.result
            break
    }
    document.innerHTML = html
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

以上代码从表单字段中读取一个文件,并将其内容显示在了网页上。如果文件的 MIME 类型表示它是一个图片,那么将其读取后保存为数据 URI,在 load 事件触发时将数据 URI 作为图片插入页面中。如果文件不是图片,则读取后将其保存为文本并原样输出到网页上。progress 事件用于跟踪和显示读取文件的进度,而 error 事件用于监控错误。

# Blob 与部分读取

某些情况下,可能需要读取部分文件而不是整个文件。为此,File 对象提供了一个名为 slice() 的方法。slice() 方法接收两个参数:起始字节和要读取的字节数。这个方法返回一个 Blob 的实例,而 Blob 实际上是 File 的超类

blob 表示二进制大对象,是 JavaScript 对不可修改二进制数据的封装类型。包含字符串的数据、ArrayBuffers、ArrayBufferViews,甚至其他 Blob 都可以用来创建 blob。Blob 构造函数可以接收一个 options 参数,并在其中指定 MIME 类型:

console.log(new Blob(['foo']))
// Blob {size: 3, type: ''}
console.log(new Blob([{"a": "b"}], {type: 'application/json'}))
// {size: 10, type: 'application/json'}
console.log(new Blob['<p>Foo</p>', '<p>Bar</p>'], {type: 'text/html'})
// {size: 20, type: 'text/html'}
1
2
3
4
5
6

利用 Blob 构造函数我们可以手动去构造一个 “文件”,指定文件的 MIME 类型,另外也可以使用 FileReader 从 Blob 中读取数据。Blob 对象有一个 size 属性和一个 type 属性,还有一个 slice() 方法用于进一步切分数据。

# 对象 URL 与 Blob

对象 URL 有时候也称作 Blob URL,是指引用存储在 File 或 Blob 中数据的 URL。对象 URL 的优点是 不用把文件内容读取到 JavaScript 也可以使用文件。只要在适当位置提供对象 URL 即可。要创建对象 URL,可以使用 window.URL.createObjectURL() 方法并传入 File 或 Blob 对象。这个函数返回的值是一个指向内存中地址的字符串。因为这个字符串是个 URL,所以可以在 DOM 中直接使用。例如,以下代码可以在页面中显示一张图片:

let iFile = document.getElementById('iFile')
iFile.addEventListener('change', (event) => {
    let file = event.target.files[0]
    const imgEle = document.createElement('img')
    imgEle.src = URL.createObjectURL(file)
    document.body.appendChild(imgEle)
})
1
2
3
4
5
6
7

如果把对象 URL 直接放到 <img> 标签中,就不需要使用 FileReader 把数据先读到 JavaScript 中了。<img> 标签可以直接从相应的内存位置把数据读取到页面上。

使用完数据之后,最好能释放与之关联的内存。只要对象 URL 在使用中,就不能释放内存。如果想表明不再使用某个对象 URL,则可以把它传给 window.URL.revokeObjectURL()。页面卸载时,所有对象 URL 占用的内存都会被释放。不过,最好在不使用时就立即释放内存,以便尽可能保持页面占用最少资源。

createObjectURL 与 FileReader.readAsDataURL 的区别

  1. 执行机制
    • createObjectURL 是同步立即执行
    • FileReader.readAsDataURL 是异步执行,需要异步读取文件内容
  2. 内存占用
    • createObjectURL 返回一个存储在内存中的 File 或 Blob 对象的 URL 地址(使用哈希值表示),并且页面卸载时清除,也可以使用 revodeObjectURL 手动清除
    • FileReader.readAsDataURL 异步读取文件内容之后,返回一个包含 base64 字符串格式的文件内容的 Data URL,通常内存占用就更大。利用垃圾回收机制清楚内存占用