xiaobaoqiu Blog

Think More, Code Less

File Magic Number

起因

最近在做一个图片相关的项目,涉及到图片的处理,在切换到新的图片服务器之后,原始的图片裁剪接口失败,报错的原因是:

1
2
3
[2014-08-25 22:05:45 ERROR XXXX.util.ImageUtil:175] cutJPG is error,input:/home/q/www/crm.fs.qunar.com/mfs-fs/image/2f/fd/new_2ffd34d0edf5dcc7807107dd9e745d2b.jpg
javax.imageio.IIOException: Not a JPEG file: starts with 0x89 0x50
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)

很奇怪,错误原因是说new_2ffd34d0edf5dcc7807107dd9e745d2b.jpg这个图片不是jpg格式的图片.

问题

老的裁剪接口使用的是java自带的javax.imageio与java.awt.image包下工具,如ImageReader, ImageWritter,BufferedImage等. 错误发生的地方的代码逻辑:

1
2
3
4
5
6
7
8
9
//根据文件后缀获取Reader
Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(StringUtil.getFileSuffix(input));
ImageReader reader = it.next();
...
//获取图片读取需要的参数
ImageReadParam param = reader.getDefaultReadParam();
...
//读取图片
BufferedImage bi = reader.read(0, param);

错误显示在最后上面代码的最后一行读取文件的时候失败,更下一步的原因是JPEGImageReader的read方法:

1
2
3
4
5
6
7
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(JPEGImageReader.java:604) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.checkTablesOnly(JPEGImageReader.java:342) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.gotoImage(JPEGImageReader.java:476) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readHeader(JPEGImageReader.java:597) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1054) ~[na:1.7.0_45]
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1034) ~[na:1.7.0_45]

ImageReader有很多的具体实现,包括PNGImageReader, BMPImageReader, GIFImageReader等,这里的原因就是一个png的图片后缀名改为jpg,然后Reader的时候会根据后缀(.jpg)来选择实际的Reader.

那么JPEGImageReader怎么知道这个图片不是一个jpg的图片呢?这就涉及到我们这里讲到的File magic number.

什么File magic number

Magic numbers are the first bits of a file which uniquely identify the type of file. This makes programming easier because complicated file structures need not be searched in order to identify the file type.

简单的说File magic number就是用文件的一些bit位来唯一标志这个文件的类型.

下面是三个图片文件(jpg, png, gif)的头部:

1
2
3
4
5
6
7
8
9
xiaobaoqiu@xiaobaoqiu:~/Picture$ hexdump -C 1.jpg -n 10
00000000  ff d8 ff e0 00 10 4a 46  49 46                    |......JFIF|
0000000a
xiaobaoqiu@xiaobaoqiu:~/Picture$ hexdump -C ticket.png -n 10
00000000  89 50 4e 47 0d 0a 1a 0a  00 00                    |.PNG......|
0000000a
xiaobaoqiu@xiaobaoqiu:~/Picture$ hexdump -C rtree.gif -n 10
00000000  47 49 46 38 39 61 37 02  8b 02                    |GIF89a7...|
0000000a

图片1.jpg中的开始的ff d8表明只是一个JPG文件,随后的ff e0表明这是一种JFIF结构类型(参考:http://blog.csdn.net/kickxxx/article/details/8173332).

GIF文件的头部包括 : (47 49 46 38 39 61)和(47 49 46 38 37 61)这两种;

PNG文件头部 : 89 50 4E 47 0D 0A 1A 0A

典型文件的File magic number

总结一些典型文件的File magic number:

图片

文件类型 典型扩展名 Hex字符 Ascii digits
Bitmap format .bmp 42 4d BM
FITS format .fits 53 49 4d 50 4c 45 SIMPLE
GIF format .gif 47 49 46 38 GIF8
Graphics Kernel System .gks 47 4b 53 4d GKSM
IRIS rgb format .rgb 01 da ..
ITC (CMU WM) format .itc f1 00 40 bb ….
JPEG File Interchange Format .jpg ff d8 ff e0 ….
NIFF (Navy TIFF) .nif 49 49 4e 31 IIN1
PM format .pm 56 49 45 57 VIEW
PNG format .png 89 50 4e 47 .PNG
Postscript format .[e]ps 25 21 %!
Sun Rasterfile .ras 59 a6 6a 95 Y.j.
Targa format .tga xx xx xx
TIFF format (Motorola - big endian) .tif 4d 4d 00 2a MM.*
TIFF format (Intel - little endian) .tif 49 49 2a 00 II*.
X11 Bitmap format .xbm xx xx
XCF Gimp file structure .xcf 67 69 6d 70 20 78 63 66 20 76 gimp xcf
Xfig format .fig 23 46 49 47 #FIG
XPM format .xpm 2f 2a 20 58 50 4d 20 2a 2f / XPM /

压缩文件

文件类型 典型扩展名 Hex字符 Ascii digits
Bzip .bz 42 5a BZ
Compress .Z 1f 9d ..
gzip format .gz 1f 8b ..
pkzip format .zip 50 4b 03 04 PK..

可执行文件

文件类型 典型扩展名 Hex字符 Ascii digits
MS-DOS, OS/2 or MS Windows 4d 5a MZ
Unix elf 7f 45 4c 46 .ELF