如何存储包含大体积二进制数据及其他字段值的JS对象?
问题描述
当前需要存储同时包含普通类型值、二进制文件数据的JS对象,预期存储结构如下:
{ "value":"xyz", "file1":"[FileContent]", "file2":"[LargeFileContent]" }
此前无二进制数据时,一直使用JSON格式存储所有内容。引入二进制数据后,处理1GB以上大文件时出现异常。
之前尝试过将二进制数据序列化为base64编码后存入JSON的方案,该方案处理20MB左右小文件时运行正常,但处理大文件时FileReader返回结果为空字符串,最终得到的异常结构如下:
{ "value":"xyz", "file1":"[FileContent]", "file2":"" }
当前读取Blob的实现代码如下:
const readFiles = async (measurements: FormData) => { setFiles([]); // 结果存储位置 let promises: Array<Promise<string>> = []; measurements.forEach((value) => { let dataBlob = value as Blob; console.log(dataBlob); // 此处打印Blob对象正常 promises.push( new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(dataBlob); reader.onloadend = function () { resolve(reader.result as string); }; reader.onerror = function (error) { reject(error); }; }) ); }); let result = await Promise.all(promises); console.log(result); // 大文件对应项为空 setFiles(result); };
原因说明
Base64编码本身存在33%的体积膨胀,再加上JS中字符串每个字符占2字节的存储开销,1GB原始文件转成base64后需要占用近2.7GB内存,很容易触发浏览器单标签页的内存限制,直接导致FileReader读取失败返回空值,这也是小文件正常、大文件异常的核心原因。
可行方案
- 方案1:使用multipart/form-data格式替代纯JSON
这是处理混合普通字段+二进制文件数据的标准方案,不需要做任何编码转换,不会产生额外体积开销,也不会触发大文件内存问题:- 普通文本字段直接按键值对存入FormData
- 二进制文件字段直接以Blob/File对象存入,无需转码
- 浏览器原生支持该格式的解析、传输,无额外依赖
如果需要本地持久化存储,可以直接将FormData对象存入IndexedDB,不需要做序列化处理。
- 方案2:分块读取转码,避免一次性加载全量文件到内存
如果必须使用JSON类结构化存储,不要一次性读取整个大文件转base64:- 将大文件按固定大小(推荐10MB/块)切分
- 逐块读取转base64,按顺序存入数组
- 存储结构中将大文件字段替换为分块后的base64数组
- 使用时再逐块拼接还原
核心分块读取逻辑参考:
该方案可以将单步内存占用控制在块大小的膨胀范围内,不会触发内存溢出。const readLargeFileInChunks = async (file: Blob, chunkSize = 10 * 1024 * 1024) => { const chunks: string[] = []; let offset = 0; while(offset < file.size) { const chunk = file.slice(offset, offset + chunkSize); const base64Chunk = await new Promise<string>((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(chunk); reader.onload = () => resolve(reader.result as string); reader.onerror = reject; }); chunks.push(base64Chunk); offset += chunkSize; } return chunks; } - 方案3:普通数据与二进制数据分离存储
不要把二进制数据塞进JSON结构:- 普通JSON结构只存储文本字段、以及二进制文件的元信息(文件名、大小、类型、存储索引)
- 大文件二进制数据直接以Blob格式存入IndexedDB、或者Cache API缓存目录
- 使用时先读取JSON拿到元信息,再根据存储索引取对应的二进制数据
该方案是前端本地存储大体积混合数据的通用最佳实践,完全规避序列化、编码带来的性能和内存问题。
不建议继续尝试全量base64编码大文件的方案,除了内存溢出问题外,转码过程还会阻塞事件循环导致页面长时间卡顿,GB级文件场景下性能表现极差。
内容的提问来源于stack exchange,提问作者flonair22
相关产品推荐
相关产品推荐

