博客

什么是文件魔数(Magic Number)?格式识别原理与常见示例

发布于 2026 年 4 月 24 日 · 阅读约 6 分钟

操作系统怎么知道文件类型?

把一个 PNG 文件改名成 .txt,用 macOS 的 Quick Look 预览,它依然能正确显示图片。在 Linux 上运行 file renamed.txt,输出依然是 PNG image data。文件名明明变了,为什么格式没变?

因为操作系统识别格式从来不靠文件名,而是读取文件开头的几个固定字节——这就是魔数(Magic Number),也叫文件签名(File Signature)。

这套机制是现代操作系统与浏览器的共同基础。

魔数是什么?

每种文件格式在设计时,都会在文件开头预留几个固定字节作为格式声明——这就是魔数(Magic Number),也叫文件签名(File Signature)或文件头(File Header)。

类比一下:你出入境时,海关不需要听你自我介绍,直接翻开护照第一页就能确认国籍。魔数就是文件的「护照首页」——不管文件叫什么名字,打开头几个字节就能确认真实身份。

这也是 Unix file 命令的工作原理:它维护了一份 magic 规则库,通过字节匹配而不是扩展名来判断格式。Windows 资源管理器、macOS Finder、浏览器 MIME 嗅探——底层逻辑都一样。文件类型由内容决定,不由名字决定。

常见格式魔数速查表

以下是开发中最常见格式的文件头签名,均来自实际字节定义:

格式 魔数(Hex) 可读形式 备注
PNG 89 50 4E 47 0D 0A 1A 0A ‰PNG.... 8 字节,最易辨认的签名之一
JPEG FF D8 FF 3 字节,第 3 字节因子类型略有差异
GIF 47 49 46 38 GIF8 后跟 37(GIF87a)或 39(GIF89a)
PDF 25 50 44 46 %PDF 后跟版本号,如 -1.7
WebP 52 49 46 46 … 57 45 42 50 RIFF…WEBP RIFF 容器,需看偏移 8–11 字节
WAV 52 49 46 46 … 57 41 56 45 RIFF…WAVE RIFF 容器,子类型为 WAVE
MP3 49 44 33 / FF Fx ID3 / sync 有 ID3 标签看前 3 字节;无标签时第 2 字节高 3 位为 111
FLAC 66 4C 61 43 fLaC 无损音频,签名清晰
MP4 / MOV / M4A 00 00 00 xx 66 74 79 70 ....ftyp ftyp 容器,需看偏移 8–11 的品牌标识(major brand)
WebM 1A 45 DF A3 EBML 容器头
OGG 4F 67 67 53 OggS 音频/视频 OGG 容器
BMP 42 4D BM Windows 位图

陷阱:只看前 4 字节会误判

表格里有两类格式需要特别注意——RIFF 容器和 ftyp 容器。它们的前几个字节相同,但实际上包含完全不同的媒体类型,只看前 4 字节会直接误判。

RIFF 容器(WebP / WAV / AVI)

WebP、WAV、AVI 都以 RIFF52 49 46 46)开头。真正的子类型藏在偏移 8–11 字节处:

+0
RIFF
固定前缀
+4
文件大小 (4 字节)
+8
子类型 WEBP / WAVE / AVI
决定真实格式
+12
chunk 数据

ftyp 容器(MP4 / MOV / M4A)

MP4、MOV、M4A 的开头 4 字节是可变的 box 大小,偏移 4–7 字节固定为 ftyp偏移 8–11 字节才是品牌码,决定具体格式:

+0
box 大小 (可变)
+4
ftyp
固定标识
+8
品牌标识 isom / M4A / qt
决定真实格式
+12
兼容列表

常见品牌码:isom / iso2 / avc1 = MP4,M4A = M4A(Apple 音频),qt = QuickTime MOV。

动手验证:用 file 命令读取魔数

在 macOS 和 Linux 上,file 命令通过读取文件原始字节并与 magic 规则库匹配来识别格式,与文件扩展名无关:

# macOS / Linux
$ file -b photo.png
PNG image data, 617 x 875, 8-bit/color RGBA, non-interlaced

$ file -b photo.txt       # 将后缀改为 .txt
PNG image data, 617 x 875, 8-bit/color RGBA, non-interlaced

file 是 macOS 和 Linux 的内置工具,Windows 没有原生等价命令。

如果需要在代码里实现相同的格式检测逻辑,核心只需要这几行 JavaScript:

// 取 Base64 字符串前 24 个字符(atob 解码后 = 18 字节),用于格式检测
// (每 4 个 Base64 字符 = 3 字节)
const b64 = "iVBORw0KGgoAAAANSUhEUgAA...";
const bin = atob(b64.slice(0, 24));
const hex = [...bin]
  .map(c => c.charCodeAt(0).toString(16).padStart(2, "0"))
  .join(" ");
console.log(hex);
// → 89 50 4e 47 0d 0a 1a 0a ...(PNG)

对照速查表:89 50 4e 47 是 PNG,ff d8 ff 是 JPEG,25 50 44 46 是 PDF。

小结

文件类型由二进制头部的魔数决定,与文件名和后缀无关。只要读取开头几个字节并与已知签名对比,就能在没有任何上下文的情况下准确识别格式。

这也是为什么改后缀名骗不过操作系统——也骗不过任何读魔数的工具。

相关文章

优雅地调试 JSON API 响应中的 Base64 图片 →

ViewJSON 在渲染 JSON 时会自动对字符串值做魔数检测,Base64 图片、音频和视频直接内联预览。

立即体验

把包含 Base64 内容的 JSON 粘进来,ViewJSON 会自动检测格式,支持图片、音频和视频一键预览。

打开 ViewJSON →