JavaScript读取二进制文件提取头部JSON的实现及优化问题
问题解答
1、仅读取文件头部指定区域的实现方法
File对象继承自Blob,原生支持slice()方法切割文件的任意片段,完全不需要加载整个100MB文件,只需要读取头部最多1MB+4字节的内容即可,操作逻辑如下:
- 因为你明确JSON部分最大为1MB,直接读取前
1024*1024 + 4 = 1048580字节(1MB+4字节存储长度)即可覆盖所有场景,一次读取完成即可 - 仅需修改读取文件的逻辑:
// 切割文件的前1MB+4字节即可满足需求 const slicedFile = file.slice(0, 1024 * 1024 + 4); reader.readAsArrayBuffer(slicedFile);
不管源文件多大,最多只会加载1MB多一点的内容到内存中。
2、JSON字符串提取逻辑的正确性和优化方案
现有逻辑仅在JSON全为单字节字符时可以正常工作,如果JSON包含中文等多字节UTF-8字符,直接用String.fromCharCode逐个字节拼接会出现乱码,并且循环拼接字符串的性能很低。
优化方案可以直接使用TextDecoder API,直接解码指定区间的二进制数据,无需循环,同时兼容UTF-8多字节字符:
// 替代原来的循环拼接逻辑 const jsonUint8 = new Uint8Array(reader.result, 4, jsonLen); const jsonStr = new TextDecoder().decode(jsonUint8); const jsonObj = JSON.parse(jsonStr);
上面的代码直接从偏移4字节的位置截取长度为jsonLen的二进制数据,直接解码为字符串,性能比循环拼接高几个数量级,也不会出现多字节字符乱码问题。
3、DataView和Uint8Array的内存占用说明
DataView和所有TypedArray(包括Uint8Array)都属于二进制视图,本身不会复制底层ArrayBuffer的数据,仅持有ArrayBuffer的引用以及自身的偏移量、长度属性,额外占用的内存可以忽略不计。
需要注意的是:如果你的ArrayBuffer是加载整个文件生成的,那么ArrayBuffer本身会占用和文件大小相当的内存,这也是为什么建议提前用slice()切割文件只读取需要的部分,从根源上减少内存占用。
优化后完整可运行代码
<html> <body> <input type="file" id="file-input" /> </body> <script> function readSingleFile(e) { const file = e.target.files[0]; if (!file) return; // 仅切割读取头部最多1MB+4字节的内容 const slicedFile = file.slice(0, 1024 * 1024 + 4); const reader = new FileReader(); reader.onload = function () { const dataView = new DataView(reader.result, 0); const jsonLen = dataView.getInt32(0, true); // 直接截取指定区间解码为UTF-8字符串 const jsonUint8 = new Uint8Array(reader.result, 4, jsonLen); const jsonStr = new TextDecoder().decode(jsonUint8); const jsonObj = JSON.parse(jsonStr); console.log(jsonObj); } reader.readAsArrayBuffer(slicedFile); } document.getElementById('file-input').addEventListener('change', readSingleFile, false); </script> </html>
内容的提问来源于stack exchange,提问作者Index
相关产品推荐
相关产品推荐

