如何判断JavaScript中ArrayBuffer存储的是文本还是二进制数据
ArrayBuffer 文本/二进制类型检测方案
没有100%准确率的通用检测方案,但通过多层逻辑组合,可以覆盖绝大多数常规场景,实测准确率可达99%以上:
1. 优先检测二进制文件魔数(Magic Number)
绝大多数标准二进制格式都有固定的文件头标识(魔数),只要命中已知二进制格式的魔数,可直接判定为二进制文件,这一步能筛掉90%以上的常用二进制资源。
示例实现:
function isKnownBinary(buffer) { // 仅读取前4字节即可覆盖绝大多数常用格式的魔数 const header = new Uint8Array(buffer, 0, 4); // PNG if (header[0] === 0x89 && header[1] === 0x50 && header[2] === 0x4E && header[3] === 0x47) return true; // JPG if (header[0] === 0xFF && header[1] === 0xD8 && header[2] === 0xFF) return true; // GIF if (header[0] === 0x47 && header[1] === 0x49 && header[2] === 0x46 && header[3] === 0x38) return true; // Windows PE 可执行文件 if (header[0] === 0x4D && header[1] === 0x5A) return true; // Linux ELF 可执行文件 if (header[0] === 0x7F && header[1] === 0x45 && header[2] === 0x4C && header[3] === 0x46) return true; // ZIP/RAR等压缩包、音视频格式等魔数可按需扩展 return false; }
2. 控制字符占比检测
纯文本文件(含各类编码)中,除了制表符0x09、换行0x0A、回车0x0D之外,基本不会出现0x00-0x1F区间的ASCII控制字符,而二进制文件中这类字符的占比通常很高。
示例实现:
function isTextByControlRatio(buffer, threshold = 0.01) { const bytes = new Uint8Array(buffer); let invalidControlCount = 0; const allowedControls = new Set([0x09, 0x0A, 0x0D]); for (const byte of bytes) { if (byte < 0x20 && !allowedControls.has(byte)) { invalidControlCount++; } } // 非法控制字符占比超过阈值则判定为二进制,阈值可根据业务调整 return invalidControlCount / bytes.length < threshold; }
3. 可选:UTF-8 编码合法性校验
如果你的业务场景中主要处理UTF-8编码的文本,可以额外增加编码校验逻辑,解码失败则大概率为二进制文件:
function isValidUtf8(buffer) { try { // fatal设为true时,遇到不符合UTF-8编码规则的字节会抛出错误 const decoder = new TextDecoder('utf-8', { fatal: true }); decoder.decode(buffer); return true; } catch { return false; } }
推荐判定流程
按以下顺序执行检测,效率最高、误判率最低:
- 第一步:执行魔数检测,命中直接判定为不可编辑二进制
- 第二步:执行控制字符占比检测,超过阈值判定为二进制
- 第三步(可选):如果以UTF-8文本为主,执行编码合法性校验,不合法判定为二进制
- 以上检测全部通过,判定为可编辑文本
内容的提问来源于stack exchange,提问作者Duke Dougal
相关产品推荐
相关产品推荐

