先记住这个答案
现代浏览器里最简单的写法是 const buffer = await blob.arrayBuffer()。arrayBuffer() 是 Blob 自带的方法,返回一个 Promise,读取完成后得到 ArrayBuffer,出错时 Promise 会 reject,可以直接用 try...catch 处理。需要兼容老浏览器时用 FileReader:调用 reader.readAsArrayBuffer(blob) 开始读,在 load 事件里通过 reader.result 拿结果,失败走 error 事件。两种方式都会把整个 Blob 读进内存,大文件要改用 blob.stream() 或者 slice 分片读取。
- 首选
await blob.arrayBuffer() FileReader要监听load和error事件- 两种方式都会把整个文件读进内存
- 大文件用
blob.stream()或slice分片读
首选写法:blob.arrayBuffer()
拿到 ArrayBuffer 后,要用 Uint8Array 这类视图才能看到具体字节。
async function inspectBlob(blob) {
const buffer = await blob.arrayBuffer() // 读完整个 Blob
const bytes = new Uint8Array(buffer) // 用视图查看字节
return { size: buffer.byteLength, bytes: [...bytes] }
}
const blob = new Blob(['Hi'], { type: 'text/plain;charset=utf-8' })
inspectBlob(blob).then(result => console.log(result))
// { size: 2, bytes: [72, 105] }'Hi' 按 UTF-8 编码是两个字节,72 和 105,所以 byteLength 是 2。忘写 await 是最常见的错误,这时 buffer 是一个 Promise,传给 new Uint8Array 会得到一个长度为 0 的数组,不会报错,很难发现。
读出来的是一份新内存,修改 bytes 不会改变原来的 blob,因为 Blob 本身是不可修改的。
兼容写法:FileReader 封装成 Promise
FileReader 是早期的读取接口,所有浏览器都支持。它不直接返回数据,要监听事件,所以一般封装成 Promise 再用。
function readAsArrayBuffer(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result) // 读取成功,result 是 ArrayBuffer
reader.onerror = () => reject(reader.error) // 读取失败
reader.onabort = () => reject(new Error('读取被取消'))
reader.readAsArrayBuffer(blob)
})
}load、error、abort 三个事件都要处理,漏掉 error 的话,读取失败时 Promise 永远不会结束。reader.result 的类型取决于调用了哪个读取方法:readAsArrayBuffer 得到 ArrayBuffer,用 readAsText 得到字符串,用 readAsDataURL 得到 data: 开头的字符串,读完之前它是 null。
容易答错的地方
- 大文件也直接
arrayBuffer() - 几个 GB 的视频一次性读成
ArrayBuffer,内存会瞬间飙升,页面可能直接崩溃。上传或处理大文件时,用blob.slice(start, end)切成小块逐块读,或者用blob.stream()拿到一个ReadableStream,边读边处理。 - 以为读出来的
ArrayBuffer和Blob共享内存 arrayBuffer()每次都返回一份新的内存。修改它不会影响Blob,想得到修改后的Blob,要用new Blob([buffer])重新创建。
面试官还会怎么问?
只想拿文本内容,也要先转 ArrayBuffer 吗?
不用。await blob.text() 直接按 UTF-8 解码成字符串。只有需要用其他编码解码,比如 GBK,或者要检查原始字节、解析二进制格式时,才先转 ArrayBuffer 再用 TextDecoder 或 DataView 处理。
怎样读取文件的前几个字节判断文件类型?
先 blob.slice(0, 4) 切出前 4 个字节,再对这一小块调用 arrayBuffer(),不会读整个文件。PNG 文件的前 4 个字节是 89 50 4E 47。但这只能用于前端提示,服务端还是要重新校验。
fetch 拿到的响应怎么转成 ArrayBuffer?
Response 对象也有 arrayBuffer() 方法,直接 await response.arrayBuffer()。也可以先 await response.blob() 得到 Blob,再按上面的方法处理。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。