在网上我们经常看见以.jpg
为扩展名的文件,却有着gif
图片的特征,绘声绘色的在浏览器里帧帧重绘。当然这是在理想的情况下,如果我们使用IE浏览器,就会发现图片只能显示成一个小红x,任凭怎样刷新都无法显示。
造成这样的结果并不是一种原因,当我们将文件的后缀名修改时,实际上文件内容并没有发生改变,比如用笔记本打开修改扩展名为JPG的GIF文件,会发现它们都拥有相同的文件头,GIF87a
,而这是GIF文件通用的文件头。
不过有幸我们能够通过webkit-like浏览器打开这张修改扩展名的gif图片,查看webkit源码我们就能发现,在webkit的ImageDecoder模块,使用的正是写死文件头的方法进行判断图片解码:
bool matchesGIFSignature(char* contents)
{
return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
}
bool matchesPNGSignature(char* contents)
{
return !memcmp(contents, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
}
bool matchesJPEGSignature(char* contents)
{
return !memcmp(contents, "\xFF\xD8\xFF", 3);
}
if (matchesGIFSignature(contents))
return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption);
而对比之下,firefox采用的Gecko引擎的判定方式就变得更加“web”了,它首先通过imgLoader.cpp
通过比对文件头方式确定文件类型,然后再赋予他们一个MIMEType常量:
if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
!nsCRT::strncmp(aContents, "GIF89a", 6)))
{
aContentType.AssignLiteral("image/gif");
}
/* or a PNG? */
else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
(unsigned char)aContents[1]==0x50 &&
(unsigned char)aContents[2]==0x4E &&
(unsigned char)aContents[3]==0x47 &&
(unsigned char)aContents[4]==0x0D &&
(unsigned char)aContents[5]==0x0A &&
(unsigned char)aContents[6]==0x1A &&
(unsigned char)aContents[7]==0x0A))
{
aContentType.AssignLiteral("image/png");
}
最后再通过Image.cpp
判断MIMETYPE常量判断应该使用哪种解码器去解析,虽然使用MIMETYPE是非常“web”的方式,firefox也曾经声称自己的存在对web更有必要,但这显然是一个脱裤子放屁式的举动,尤其是当你看到Image目录结构和webkit条理清晰的Decoder/Encoder安排之后:
Image::GetDecoderType(const char *aMimeType)
{
// By default we don't know
eDecoderType rv = eDecoderType_unknown;
// PNG
if (!strcmp(aMimeType, "image/png"))
rv = eDecoderType_png;
else if (!strcmp(aMimeType, "image/x-png"))
rv = eDecoderType_png;
// GIF
else if (!strcmp(aMimeType, "image/gif"))
rv = eDecoderType_gif;
// JPEG
else if (!strcmp(aMimeType, "image/jpeg"))
rv = eDecoderType_jpeg;
else if (!strcmp(aMimeType, "image/pjpeg"))
rv = eDecoderType_jpeg;
else if (!strcmp(aMimeType, "image/jpg"))
rv = eDecoderType_jpeg;
// BMP
else if (!strcmp(aMimeType, "image/bmp"))
rv = eDecoderType_bmp;
else if (!strcmp(aMimeType, "image/x-ms-bmp"))
rv = eDecoderType_bmp;
// ICO
else if (!strcmp(aMimeType, "image/x-icon"))
rv = eDecoderType_ico;
else if (!strcmp(aMimeType, "image/vnd.microsoft.icon"))
rv = eDecoderType_ico;
// Icon
else if (!strcmp(aMimeType, "image/icon"))
rv = eDecoderType_icon;
return rv;
}
了解了其他两种方式之后,我们再来看IE。虽然是黑盒我们看不到答案的来龙去脉,但获取头文件确定解码方式是现行的两种浏览器的主流方式,显然IE并没有这么做。通过右键图片属性的,很可能是通过文件协议直接读取扩展名,导致了原本是GIF的文件却通过JPG的编码方式读取,得到的结果必然是一张小红叉。
以上情况在IE6 - IE10中均有存在,当然国内使用IE8内核的360安全浏览器里也有这样的情况了。