用 fileinputstream 读前 8 字节比对 magic number 即可判断图片格式,如 png(0x89 0x50 0x4e 0x47)、jpeg(0xff 0xd8),需强转十六进制为 byte 并检查 read 返回值防越界,避免使用字符流或 imageio.read()。

怎么用 Java IO 流读取图片文件头判断格式
直接读前几个字节就行,不用加载整张图。不同格式的图片在文件开头有固定签名(magic number),比如 JPEG 是 0xFF 0xD8,PNG 是 0x89 0x50 0x4E 0x47。
实操建议:
- 用
FileInputStream打开文件,调用read(byte[])读前 8 字节足够覆盖常见格式(GIF、JPEG、PNG、WEBP) - 别用
BufferedReader或字符流——图片是二进制,用字符流会乱码或截断 - 读完立刻
close(),或者用 try-with-resources,否则小文件测不出问题,批量处理时容易触发Too many open files
常见图片头字节和 Java 判断逻辑怎么写
硬记十六进制容易错,直接比对 byte 数组更稳。注意:Java 的 byte 是有符号的(-128~127),但文件头是无符号字节,比较时要么转成 int 再 & 0xFF,要么用预定义的 byte 值直接比。
示例片段(判断 PNG 和 JPEG):
byte[] header = new byte[8];
int read = fis.read(header);
if (read >= 4 && header[0] == (byte)0x89 && header[1] == (byte)0x50 && header[2] == (byte)0x4E && header[3] == (byte)0x47) {
return "PNG";
}
if (read >= 2 && header[0] == (byte)0xFF && header[1] == (byte)0xD8) {
return "JPEG";
}
常见错误现象:
- 用
==直接比0x89这种字面量 → 实际得到的是-119,必须强转(byte)0x89 - 没检查
read返回值就访问header[3]→ 小于 4 字节的损坏文件会抛ArrayIndexOutOfBoundsException - 把
WEBP头RIFF****WEBP当字符串比 → 用new String(header)会因编码问题失效,坚持字节比
为什么不用 ImageIO.read() 而要手动读头
ImageIO.read() 是为了加载并解码图像,它会尝试解析整个文件结构,遇到不支持的格式、损坏数据或恶意构造的头部,可能卡住、OOM 或抛出模糊异常(比如 IIOException: Unsupported Image Type)。
而只读头信息的场景,比如:
- 上传接口快速校验文件类型(绕过扩展名欺骗)
- 扫描大量文件做初步分类,跳过非图片
- 嵌入式或资源受限环境,避免引入完整图像解码栈
性能差异明显:读 8 字节是 O(1),ImageIO.read() 至少要读几十 KB 甚至更多,还依赖 SPI 注册的插件。
Windows 和 Linux 下路径/编码会影响文件头读取吗
不会。文件头是原始字节,跟路径分隔符、系统默认编码完全无关。但有两个实际坑点:
- 传入的
File或路径字符串本身为空或不存在 → 抛FileNotFoundException,不是文件头问题,得提前校验 - 用
Paths.get("中文.jpg")在某些旧 JDK + Windows 控制台环境下可能因终端编码导致路径乱码 → 文件打不开,但这是路径构造阶段的问题,和读头逻辑无关 - 如果用
getResourceAsStream()从 jar 包里读,确保资源路径正确且没被优化掉;jar 内路径不能用File,要用流
真正容易被忽略的是:有些“图片”其实是带 ZIP 尾部的复合文件(比如 DOCX 改后缀),头是对的,但后面根本不是图像数据——仅靠头校验只能防伪,不能保真。










