FileReader.readAsArrayBuffer是首选,因它原生支持File(Blob子类)的原始字节提取,兼容性好、无需服务端参与;相比DataURL或fetch方案更可靠安全。

直接用 FileReader 读取为 ArrayBuffer,这是最可靠、兼容性最好、且无需服务端参与的方式。
为什么 FileReader.readAsArrayBuffer 是首选
上传的 File 对象本质是 Blob 的子类,而 FileReader 原生支持将其内容以原始字节形式加载进内存。相比先转成 DataURL 再 atob 解码(易出错、吃内存、不支持二进制非文本内容),或依赖 fetch + Response.arrayBuffer()(需要伪造请求、绕过真实文件流),readAsArrayBuffer 是唯一面向本地文件设计的同步字节提取路径。
常见错误现象:atob 解码失败报 “The string to be decoded is not correctly encoded”;或 Uint8Array.from(atob(dataUrl.split(',')[1]), c => c.charCodeAt(0)) 遇到中文/emoji 直接乱码——因为 DataURL 是 base64 编码的 UTF-8 字节,不是字符串。
- 必须监听
onload,而非onloadend:只有onload中的event.target.result才是有效的ArrayBuffer -
FileReader实例不能复用:每次读取新文件前需新建实例 - 大文件(>100MB)慎用:
ArrayBuffer会完整载入内存,可能触发浏览器 OOM
怎么用 DataView 安全读取结构化字段
拿到 ArrayBuffer 后,不能直接访问字节——它只是内存块。必须通过视图(Uint8Array 或 DataView)才能读写。若文件有固定头(如 PNG、ZIP、WAV),DataView 更合适,因为它能精确控制字节序和读取类型。
立即学习“前端免费学习笔记(深入)”;
例如解析 PNG 宽度(位于偏移 16 开始的 4 字节无符号整数,网络序/big-endian):
const dv = new DataView(arrayBuffer); const width = dv.getUint32(16, false); // false = big-endian
容易踩的坑:
- 偏移量算错:PNG 签名占 8 字节,之后是 IHDR chunk,长度 4 字节 + 类型 4 字节 + 数据 13 字节 → 宽度在第 16 字节起(从 0 开始计)
- 字节序混淆:小端设备上读大端数据必须显式传
false,否则值错乱 - 越界读取:
dv.getUint32(1000)不报错但返回 0,需提前校验arrayBuffer.byteLength >= 1004
大文件怎么避免内存爆炸
一次性读整个 File 到 ArrayBuffer 在处理视频、数据库导出包等百 MB 级文件时极易卡死。正确做法是用 Blob.slice() 分块,每次只加载几 MB:
-
file.slice(start, end)返回新Blob,再喂给FileReader - 适合场景:校验文件末尾的 ZIP central directory、提取 MP4 moov box、跳过 PNG IDAT 数据块
- 注意:
slice的end是开区间,file.slice(0, 1024)取前 1024 字节(索引 0~1023) - 不要在循环中连续调用
readAsArrayBuffer:需等上一块onload触发后再读下一块,否则状态冲突
嵌入的 UTF-8 文本怎么安全解码
很多二进制格式(如 PDF 元数据、PE 资源段、自定义打包格式)内部混有 UTF-8 字符串。不能用 String.fromCharCode(...) 拼接,必须用 TextDecoder:
const decoder = new TextDecoder('utf-8');
const str = decoder.decode(new Uint8Array(arrayBuffer, offset, length));
关键点:
- 必须用
Uint8Array构造子视图:直接传arrayBuffer会解码全部内容 - 如果字符串跨块(比如流式读取中某字段被切在两块中间),必须启用
{ stream: true }并在每块后调用decoder.decode(chunk, { stream: true }) -
TextDecoder会自动处理 BOM 和非法 UTF-8 序列(默认替换为 ),比手动解析健壮得多
真正容易被忽略的是:哪怕你只读一个字段,只要它声明长度为 N 字节,就必须严格取 N 字节送入 TextDecoder —— 多一个或少一个字节都可能导致解码失败或截断。



















