JavaScript实现文件与二进制数据互转时文件损坏报错排查
问题说明
我通过<input />元素获取到存储在JavaScript File对象中的图片,希望将该文件转换为二进制字符串(非base64格式),再将得到的二进制数据还原为图片。
初始实现代码:
const file = input.files[0] const reader = new FileReader() reader.readAsBinaryString(file) reader.onloadend = () => { const binaryString = reader.result // Binary string. const newFile = new File([binaryString], file.name, { type: file.type }) // Re-create file from binary data. }
为生成的newFile调用FileReader.readAsDataURL生成data URL,通过该URL下载文件并尝试打开时,收到错误提示:
Not a JPEG file: starts with 0xef 0xbf
对原始file对象直接调用readAsDataUrl时一切正常,初步判断问题出在readAsBinaryString(file)调用或是new File([binaryString])实例化环节。
另外已知readAsBinaryString已被废弃,尝试使用readAsArrayBuffer实现相同逻辑时,出现了完全相同的问题。
错误原因
核心问题是字符串编码转换错误:
- JavaScript 原生字符串为UTF-16编码,当你把普通字符串传入
File/Blob构造函数时,构造函数会默认将字符串按UTF-8编码序列化为字节流,而非直接将每个字符的低8位作为原始二进制字节写入。 - JPEG文件的起始字节是
0xFF 0xD8,这两个值不属于合法的UTF-8单字节范围,在编码转换过程中会被替换为Unicode替换字符U+FFFD,该字符的UTF-8编码为0xEF 0xBF 0xBD,这就是报错里提到文件以0xef 0xbf开头的原因——原始文件头已经被错误编码破坏了。 - 你用
readAsArrayBuffer复现问题,是因为你大概率拿到ArrayBuffer后先将其解码为普通字符串再传入File构造函数,触发了完全相同的编码转换错误。
正确实现方案
推荐方案:直接使用ArrayBuffer(符合标准,无兼容问题)
readAsBinaryString已经从Web标准中移除,优先使用readAsArrayBuffer读取原始二进制,读取结果不要转为普通字符串,直接传入File构造函数即可,不会触发任何编码转换:
const file = input.files[0] const reader = new FileReader() reader.readAsArrayBuffer(file) reader.onloadend = () => { const arrayBuffer = reader.result // 直接传入ArrayBuffer,无编码损耗 const newFile = new File([arrayBuffer], file.name, { type: file.type }) // 生成的newFile与原文件完全一致,可正常读取、下载 }
兼容二进制字符串的方案
如果业务场景必须使用readAsBinaryString返回的二进制字符串,不要直接将字符串传入File构造函数,需要先手动将字符串转换为Uint8Array,逐字节写入原始字符码,避免UTF-8自动转码:
const file = input.files[0] const reader = new FileReader() reader.readAsBinaryString(file) reader.onloadend = () => { const binaryString = reader.result const uint8 = new Uint8Array(binaryString.length) for (let i = 0; i < binaryString.length; i++) { uint8[i] = binaryString.charCodeAt(i) } const newFile = new File([uint8], file.name, { type: file.type }) // 生成的newFile与原文件一致 }
内容的提问来源于stack exchange,提问作者Michal
相关产品推荐
相关产品推荐

