
什么是文件「魔数」从is-png-cj看懂PNG文件签名识别原理入门【免费下载链接】is-png-cj一个判断图片格式的库根据图片的文件数据判断图片是否为png格式项目地址: https://gitcode.com/Cangjie-TPC/is-png-cjis-png-cj是一个用仓颉语言Cangjie编写的轻量级文件识别库它通过读取文件头部的 8 个字节——也就是「魔数」Magic Number——来判断一个文件是否真的是PNG 图片格式。本文带你从 0 理解什么是文件魔数、PNG 签名长什么样以及 is-png-cj 如何用极简代码实现可靠的图片格式识别核心文件仅 26 行适合刚接触文件处理的新手阅读。一句话先记住魔数 写在文件开头、用来自报家门的一串固定字节。为什么不能只看文件后缀我们平时靠.png、.jpg这类后缀区分图片但后缀其实是可以被随意改掉的标签把一个可执行文件改名为可爱猫咪.png只校验后缀的程序照样会放行上传接口若只看后缀就可能被伪装成图片的文件绕过文件被截断或重命名后后缀与真实内容可能完全对不上。真正可靠的判断方式是打开文件看它开头的数据。这正是魔数登场的地方。对比项看文件后缀看文件头部魔数能否被重命名欺骗❌ 能✅ 不能需要读取多少数据0 字节只看文件名前几个字节判断依据命名习惯格式规范定义的固定签名什么是魔数文件自带的身份证魔数Magic Number也叫文件签名File Signature / Magic Bytes是每种文件格式在规范中约定的、出现在文件起始位置的固定字节序列。它就像身份证号——文件一出生就带着改名字也改不掉。先睹为快几个常见格式的魔数十六进制表示格式魔数十六进制可读部分PNG89 50 4E 47 0D 0A 1A 0A..PNG\r\n..JPEGFF D8 FF—GIF47 49 46GIFZIP / JAR50 4B 03 04PK.. 彩蛋ZIP 魔数里的50 4B正好是英文 PK——压缩软件创始人 Phil Katz 名字的首字母。PNG 的 8 字节签名长什么样PNG 规范在文件最开始定义了 8 字节的签名也就是 is-png-cj 用来比对的那份魔数模板定义在 src/is_png.cj字节位置十六进制对应 ASCII189非打印字符250P34EN447G50D回车符\r60A换行符\n71A非打印字符80A换行符\n注意中间 3 个字节50 4E 47拼出来正好是PNG三个字母签名里又特意混入回车换行0D 0A 1A 0A是为了在早期的 ASCII 文本传输场景中降低文件被破坏的概率。这种可读 不可读混合的签名设计正是魔数的经典套路。从源码看懂 is-png-cj 的识别原理 is-png-cj 的核心思路朴素到可以一眼看穿只读文件开头 8 个字节与魔数逐位比对全部一致才是 PNG。1. 用规则模板描述魔数在 src/util.cj 中库定义了Rule规则类型和Pattern规则数组ByteMatch(b)这一位必须是指定字节bAnyByte这一位任意字节都可以。这样设计的好处是模板天然可扩展——将来遇到某字节位允许变化的格式时直接把对应位改成AnyByte即可。当前 PNG 魔数 8 位全部采用ByteMatch精确匹配。2. 逐字节比对只读 8 个字节真正的比对逻辑封装在startsWith函数里提供两个版本分别对应两种输入输入流版本 src/util.cj从InputStream中按需读取头部字节字节序列版本 src/util.cj直接遍历IterableByte。它从第一个字节开始逐个与模板比对一旦发现不一致立即返回false8 位全部通过才返回true。这意味着✅ 最多只读 8 个字节哪怕是一个几百 MB 的大图判断成本几乎为零✅ 空文件、被截断的文件不足 8 字节都会被正确判为不是 PNG。3. 两个开箱即用的接口对外只暴露一个函数isPng通过参数类型自动区分两种用法见 src/is_png.cjisPng(inputStream: InputStream)—— 判断文件流isPng(iter: IterableByte)—— 判断内存中的字节序列比如从网络请求拿到的二进制数据。3 步上手判断一个文件是不是 PNG 第 1 步添加依赖。在 cjpm.toml 的[dependencies]中添加is_png_cj依赖即可当前版本 0.2.0完整写法参考 README.md 的引入依赖一节版本演进见 CHANGELOG.md。第 2 步调用isPng。官方示例 examples/is_png/src/main.cj 演示了最简用法核心就几行import is_png import std.io.* import std.fs.* main() { let f File(test.png, Open(true, false)) println(if (isPng(f)) { 是 PNG 格式 } else { 不是 PNG 格式 }) f.close() }第 3 步看结果。只要test.png开头的 8 个字节与 PNG 签名一致程序就会输出是 PNG 格式一行判断替代了看后缀、用软件打开的土办法。单元测试100% 覆盖是怎么做到的is-png-cj 的测试覆盖率是 100%。测试文件 src/is_png_test.cj 把正例和反例都钉死了测试场景输入期望结果合法 PNG 签名完整 8 字节魔数true真实 PNG 图片1×1 像素 PNGBase64 解码true空输入空数组 / 空流false签名被截断只有前 4 字节89 50 4E 47false末字节被篡改第 8 字节从0A改成00false哪怕只有1 个字节不对is-png-cj 也会果断返回false——这就是魔数识别要么全对要么不认的严谨性。魔数在开发中的实际应用场景 理解了原理你会发现魔数判断在工程中无处不在上传接口安全校验先验魔数再落盘防止伪装成图片的恶意文件混入服务器未知文件验明正身拿到一个没有后缀的文件读前几个字节对照签名表就能认出它的真实格式文件恢复文件丢失后缀后扫描数据块开头的魔数可以把散落的图片捡回来。is-png-cj 正是这一类工具的最小可用实现只聚焦 PNG 一种格式把读 8 字节 比对这件事做到极致简单。常见问题 FAQQ1魔数判断为true文件就一定是一张完好的 PNG 图片吗不完全是。魔数只能证明文件声称自己是 PNG即头部签名合法后续数据块是否完整、能否正常解码还要交给图像解码器验证。但作为第一步过滤它已经排除了绝大多数非 PNG 文件。Q2用流判断会不会把整个文件读完不会。isPng最多只读取开头的 8 个字节就返回。但注意它确实会推进流的读取位置若之后还要继续读原文件需要先处理流位置——接口注释里也专门提醒了这一点。Q3能不能用它判断 JPEG、GIF目前 is-png-cj 只内置了 PNG 魔数模板。不过 src/util.cj 里的PatternstartsWith是一套通用的魔数匹配引擎理论上换一份签名模板就能扩展到其他格式。写在最后回到开头的问题文件魔数就是写在文件最前面、用来自报家门的固定字节签名。is-png-cj 用极简的公开接口展示了这套机制的完整闭环定义 8 字节模板 → 逐字节比对 → 输出true/false并配上 100% 覆盖的单元测试。下次再遇到改后缀就能骗过程序的场景记得先看看文件开头的几个字节——那才是它真正的身份证。关键文件作用src/is_png.cj核心入口PNG 魔数模板与isPng接口src/util.cj通用魔数匹配引擎规则、模板、比对src/is_png_test.cj单元测试正例/反例全覆盖examples/is_png/src/main.cj可运行的功能示例README.md项目介绍与完整使用说明【免费下载链接】is-png-cj一个判断图片格式的库根据图片的文件数据判断图片是否为png格式项目地址: https://gitcode.com/Cangjie-TPC/is-png-cj创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考