JavaScript 通过二进制数据获取图片真实格式
前端开发中仅靠文件后缀名判断图片格式存在安全隐患,恶意用户可修改后缀绕过校验。本文介绍如何通过 FileReader 读取文件二进制数据,对比 JPG、PNG、GIF、BMP、WebP 等位图的魔数标识,以及 SVG 的 XML 标签特征,从而准确获取图片的真实格式,防止文件伪造。
主流浏览器目前支持七种图片格式:jpg、png、gif、bmp、ico、webp 和 svg。其中前六种属于位图,而 svg 是唯一的矢量图格式。
不同格式的图片拥有独特的优缺点及数据结构。本文旨在基于图像的二进制数据,解析并获取图片的真实格式信息。
常见方式判断图片格式
在前端开发处理图片上传功能时,常规做法是利用文件后缀名进行格式判断,参考以下代码实现:
input.addEventListener('change', (e) => {
const file = e.target.files[0]
const format = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase()
}, false)上述代码通过监听上传控件的 change 事件获取文件对象,提取文件名并截取最后一个点之后的后缀名,将其作为图片格式的依据。
这种判断方式虽常见且易于理解,但在用户手动修改文件后缀名的情况下将失效,无法保证判断的准确性。
例如,GIF 格式图片的位深度通常为 8。若将位深度为 32 的 PNG 图片后缀强制改为 gif,该文件在系统中仍显示为 GIF 格式且可正常打开。

如图所示,将 PNG 文件后缀改为 gif 后,系统信息虽显示格式为 gif,但位深度仍保持 32,其本质结构并未改变。
此时仅依赖后缀名已无法准确识别格式,需借助前端二进制相关知识,通过读取文件底层数据来确定真实格式,具体可参考前端字节二进制知识及相关 API 的介绍。
修改后缀名的方式
- 多种位图格式之间相互修改后缀名后,图片通常仍能正常显示和使用。
- 若将 GIF 动图的后缀名改为其他位图格式,动态效果将失效,图片退化为静态图。
- 若将位图格式的后缀名改为矢量图 svg,图片将无法显示或使用。
- 反之,若将 SVG 图片的后缀名改为位图格式,图片同样会失效无法使用。
图像数据简单说明
不同格式的图片存储的数据结构各异,每种格式都有其特定的数据头部特征。
通过分析各格式图像特定的数据结构,我们可以利用类型数组中的图像数据来判断图片的真实格式。
- 以 JPG 格式为例,其数据结构前两个字节固定为
0xFFD8,第三个字节通常也为固定值0xFF。 - 对于 PNG 格式,其数据结构前 8 个字节构成了 PNG 文件署名域,能明确标识当前图像为 PNG 格式。
- BMP 格式的数据结构前 14 个字节存储文件头信息,其中前 2 个字节记录了文件类型标识:
BM。 - WebP 格式需从文件头部偏移 8 个字节后,读取接下来的 4 个字节信息,该信息代表文件类型:
WEBP。
针对不同位图数据的判断,可参考下表列出的字节标识方式:
| 格式 | 标识的字节数 | 对应的十进制值 | 偏移量 |
|---|---|---|---|
| jpg | 3 | 255 216 255 | 0 |
| png | 8 | 137 80 78 71 13 10 26 10 | 0 |
| gif | 3 | 71, 73, 70 | 0 |
| webp | 4 | 87, 69, 66, 80 | 8 |
| ico | 4 | 0, 0, 1, 0 | 0 |
| bmp | 2 | 66 77 | 0 |
表中偏移量为 0 表示直接读取文件头部的几个字节;WebP 的偏移量为 8,表示跳过前 8 个字节后读取后续 4 个字节的标识符。
上述表格列出了当前浏览器支持的位图图像及其字节判断标识,通过读取并对比相应数据即可确定真实格式。
在上述格式中,BMP、GIF 和 WebP 获取的数据均对应其特有的署名标识。前文提到的 BM 和 WEBP 分别对应 BMP 和 WebP,而 GIF 格式的标识则为 GIF。可运用字符编码知识,例如使用 String.fromCharCode 方法将数值转换为字符,具体前端字符编码知识可参考前文关于字符编码的讲解。
// bmp
String.fromCharCode(66) // B
String.fromCharCode(77) // M
// gif
String.fromCharCode(71) // G
String.fromCharCode(73) // I
String.fromCharCode(70) // F
// webp
String.fromCharCode(87) // W
String.fromCharCode(69) // E
String.fromCharCode(66) // B
String.fromCharCode(80) // P
GIF 格式的署名标识需结合版本号处理,通常前 6 个字节标识为:'G'、'I'、'F'、'8'、'7(9)'、'a'。其中第 5 个字节的取值可以是 7 或 9,分别代表 1987 年版本和 1989 年版本。
JS读取图片真实格式
了解前端二进制知识后,可知图片文件可通过 Web API 对象读取对应数据,具体实现如下:
const reader = new FileReader()
reader.onload = () => {
const imgArrayBuffer = reader.result
const imgUint8Array = new Uint8Array(imgArrayBuffer)
}
reader.readAsArrayBuffer(file)
上述代码利用 FileReader 对象读取文件数据,此处将其作为 ArrayBuffer 类型读取,随后转换为类型数组以便处理。
获取图片文件的 Uint8Array 类型数组数据后,依据上文表格中的格式字节标识,以 JPG、BMP 和 WebP 为例进行判断:
imgUint8Array[0] === 66 && imgUint8Array[1] === 77 // bmp 格式
imgUint8Array[0] === 255 && imgUint8Array[1] === 216 && imgUint8Array[3] === 255 // jpg 格式
imgUint8Array[8] === 87 && imgUint8Array[9] === 69 && imgUint8Array[10] === 66 && imgUint8Array[10] === 80 // webp 格式
至此,即可通过这种方式读取图片的真实格式,部分判断代码如下:
// 各格式对应图像数据的标识数值
const IMAGEFORMATS = [
{ ext: 'png', data: [137, 80, 78, 71, 13, 10, 26, 10] },
{ ext: 'jpg', data: [255, 216, 255] },
{ ext: 'gif', data: [71, 73, 70] },
{ ext: 'ico', data: [0, 0, 1, 0] },
{ ext: 'bmp', data: [66, 77] },
{ ext: 'webp', data: [87, 69, 66, 80], offset: 8 }
]
// 循环判断文件是否符合某个格式对应的标识数值
for (let i = 0; i < IMAGEFORMATS.length; i++) {
const { data, offset, ext } = IMAGEFORMATS[i]
if (isEqualFormatPrefix(imgUint8Array, data, offset)) {
return ext
}
}不过,上述方式主要针对位图格式。若处理 SVG 图片,逻辑会稍显复杂,需另行处理。
svg格式的判断
SVG 格式图片属于矢量图,其数据通常使用 xml 标记语言描述。因此,读取图像数据后,需检查对应的标识署名为 <svg,若图像数据中包含该标识,则可大致判定为 SVG 格式图片。
<svg 标识包含 4 个符号和字母,对应的 ASCII 数值为:60, 115, 118, 103。接下来需判断图像文件是否包含相同的数据序列。
imgUint8Array[0] === 60 && imgUint8Array[1] === 115 && imgUint8Array[3] === 118 && imgUint8Array[3] === 103 // svg 格式
上述代码即为简单的 SVG 格式判断逻辑。
然而,常见的 SVG 图片数据开头通常包含 XML 标记语言的 <?xm 标签,此时需结合格式特征进一步判断:
if (isEqualFormatPrefix(fileUint8Array, [60, 63, 120, 109], offset)) { // 判断是否以 <?xm 开头
if (isHasSignCodes(fileUint8Array, [60, 115, 118, 103])) { // 判断是否包含 <svg 标签
return'svg'
}
}
注意:上述针对 SVG 矢量图的判断方式,是基于 xml 标记语言的标签符号进行的,仅能处理通过更改后缀名伪造的图片文件。若伪造一个包含 <svg 标签标识的假文件,则可能逃避此类判断。
总结
在浏览器支持的图片格式中,除 SVG 外,其他几种位图格式均可通过读取图像二进制数据的方式准确判断图片真实格式,从而有效防止文件伪造绕开校验,避免由此引发的异常问题。
到此这篇关于 JavaScript 实现获取图片文件真实格式的示例代码的文章就介绍到这了,更多相关 JavaScript 获取图片文件真实格式内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS 鼠标滚轮事件频繁触发导致卡顿怎么优化?
2026-11-05 17:08:07
优化JS回到顶部性能:防抖节流与requestAnimationFrame应用
2026-11-05 16:33:51
JS中Array、Set与Map的区别及应用场景选择
2026-11-05 16:03:02
PixiJS 常见报错排查:纹理跨域、黑屏与内存泄漏解决
2026-11-05 10:05:21
JS数组find与filter取值区别及性能对比
2026-11-05 08:39:21








