SDWebImage探究(六) —— 图片类型判断深入研究

版本记录

版本号 时间
V1.0 2018.02.10

前言

我们做APP,文字和图片是绝对不可缺少的元素,特别是图片一般存储在图床里面,一般公司可以委托第三方保存,NB的公司也可以自己存储图片,ios有很多图片加载的第三方框架,其中最优秀的莫过于SDWebImage,它几乎可以满足你所有的需求,用了好几年这个框架,今天想总结一下。感兴趣的可以看其他几篇。
1. SDWebImage探究(一)
2. SDWebImage探究(二)
3. SDWebImage探究(三)
4. SDWebImage探究(四)
5. SDWebImage探究(五)

图片格式的判断

这个其实在SDWebImage探究(四)中提到过,但是只是给出了出处以及实现,现在我们就继续深入其中,探究一下为什么要这么做。为了说明方便我们还是先给出源码,是一个NSData的分类 。

1. NSData+ImageContentType.h
#import <Foundation/Foundation.h>
#import "SDWebImageCompat.h"

typedef NS_ENUM(NSInteger, SDImageFormat) {
    SDImageFormatUndefined = -1,
    SDImageFormatJPEG = 0,
    SDImageFormatPNG,
    SDImageFormatGIF,
    SDImageFormatTIFF,
    SDImageFormatWebP
};

@interface NSData (ImageContentType)

/**
 *  Return image format
 *
 *  @param data the input image data
 *
 *  @return the image format as `SDImageFormat` (enum)
 */
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;

@end
2. NSData+ImageContentType.m
#import "NSData+ImageContentType.h"


@implementation NSData (ImageContentType)

+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
    if (!data) {
        return SDImageFormatUndefined;
    }
    
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return SDImageFormatJPEG;
        case 0x89:
            return SDImageFormatPNG;
        case 0x47:
            return SDImageFormatGIF;
        case 0x49:
        case 0x4D:
            return SDImageFormatTIFF;
        case 0x52:
            // R as RIFF for WEBP
            if (data.length < 12) {
                return SDImageFormatUndefined;
            }
            
            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                return SDImageFormatWebP;
            }
    }
    return SDImageFormatUndefined;
}

@end

这个实现的代码不多,就几行,看着还是很清晰的。

首先我们需要知道怎么区分一张图片的类型,每一张图片是什么?其实就是一个矩阵,每一个元素都是十六进制或者二进制的表示,为什么不同的软件可以针对不同的图片做不同的处理,它是怎么识别图片的类别呢?其实这就要看该图像数据的第一个字节了。

从代码中我们可以看到:

- JPEG 0xFF
- PNG 0x89
- GIF   0x47
- TIFF 0x49或0x4D
- R as RIFF for WEBP 0x52

这里每一种类型都有行业标准确定的表示字符进行标识,其实就是文件头标识,长度就是一个字节。比较特殊的是0x52,在判断是这个标识符以后,作者还进行了data长度的判断,长度小于12就返回未定义格式SDImageFormatUndefined,大于12就利用方法NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];进行长度的截取,这里面大家可以看到作者用了不怎么常用的NSData的subdataWithRange:方法,进行了长度的截取并返回了一个NSString类型的字符串,然后对这个字符串的首缀和尾缀进行判断,如果首缀是RIFF,尾缀是WEBP,就返回这是一个WEBP格式的图片,否则还是返回未定义。具体的流程就是这样的。

需要说明的是:

if (data.length < 12) {
    return SDImageFormatUndefined;
}

像上面这种容错处理和提高性能的代码是值得我们学习的。

你可能觉得说这些就完了吗?下面我们继续深入和扩展。


图片格式判断深入

下面我们就继续深入。

我们先看一下不同格式图片的文件头标识

JPEG

  • 文件头标识 (2 bytes): 0xff, 0xd8 (SOI) (JPEG 文件标识)
  • 文件结束标识 (2 bytes): 0xff, 0xd9 (EOI)

所以代码中用oxff作为JPEG格式图片的判断。

PNG

  • 文件头标识 (8 bytes) 89 50 4E 47 0D 0A 1A 0A

所以代码中用ox89作为PNG格式图片的判断。

GIF

  • 文件头标识 (6 bytes) 47 49 46 38 39(37) 61 G I F 8 9 (7) a

所以代码中用0x47作为GIF格式图片的判断。

BMP

  • – 文件头标识 (2 bytes) 42 4D B M

代码中未有识别该类型的图片,如果是该类型一致返回的就是未定义。

TIFF

  • 文件头标识 (2 bytes) 4D 4D 或 49 49

所以代码中用0x49或者0x4D作为TIFF格式图片的判断。

ICO

  • 文件头标识 (8 bytes) 00 00 01 00 01 00 20 20

还有很多的图片格式都有对应的文件标识符。

上面列出来的是几个常用的,下面我们就看一下更广泛的范围的文件头。

// 常用文件的文件头如下(16进制):

JPEG (jpg),文件头:FFD8FFE0或FFD8FFE1或FFD8FFE8

GIF ([gif](https://baike.baidu.com/item/gif/217778)),文件头:47494638PNG ([png](https://baike.baidu.com/item/png/174154)),文件头:89504E47

TIFF (tif),文件头:49492A00

Windows Bitmap ([bmp](https://baike.baidu.com/item/bmp)),文件头:424DC001

CAD ([dwg](https://baike.baidu.com/item/dwg/5953048)),文件头:41433130

Adobe Photoshop (psd),文件头:38425053

Rich Text Format ([rtf](https://baike.baidu.com/item/rtf/13211185)),文件头:7B5C727466

XML (xml),文件头:3C3F786D6C

HTML (html),文件头:68746D6C3E

Email [thorough only] (eml),文件头:44656C69766572792D646174653A

Outlook Express (dbx),文件头:CFAD12FEC5FD746F

Outlook (pst),文件头:2142444E

MS Word/Excel (xls.or.doc),文件头:D0CF11E0

MS Access ([mdb](https://baike.baidu.com/item/mdb/9688610)),文件头:5374616E64617264204A

WordPerfect (wpd),文件头:FF575043

Adobe Acrobat ([pdf](https://baike.baidu.com/item/pdf/317608)),文件头:255044462D312E

Quicken (qdf),文件头:AC9EBD8F

Windows Password (pwl),文件头:E3828596

ZIP Archive ([zip](https://baike.baidu.com/item/zip/15417954)),文件头:504B0304

RAR Archive ([rar](https://baike.baidu.com/item/rar/2502036)),文件头:52617221

Wave ([wav](https://baike.baidu.com/item/wav/218914)),文件头:57415645

AVI ([avi](https://baike.baidu.com/item/avi/213655)),文件头:41564920

Real Audio (ram),文件头:2E7261FD

Real Media (rm),文件头:2E524D46

MPEG ([mpg](https://baike.baidu.com/item/mpg/213809)),文件头:000001BA

MPEG (mpg),文件头:000001B3

Quicktime (mov),文件头:6D6F6F76

Windows Media ([asf](https://baike.baidu.com/item/asf/3918)),文件头:3026B2758E66CF11

MIDI (mid),文件头:4D546864

看完这个,大家就会明白为什么WEBP格式的只判断第一个字符0x52为什么不可以了,因为0x52为文件头的文件也可能会是rar等类型,所以这里只是判断文件头明显就不够了,我们需要更多的判断,来确定WEBP格式的唯一特征。

那么还要从WEBP格式文件的文件头入手,因为我们只能从里面的数据找到特征,确定其唯一性。

可以看见,这里是12个字节,前四个字节是RIFF,后四个字节是WEBP,这就是作者代码中要截取12个字节的原因,根据这个12个字节的前缀和后缀,可以确保其WEBP格式的唯一性不会出现误判的操作。

那么可能有人还有问题,为什么作者只判断了这5个类型?其实这好理解,JPEG和PNG就不多说了吧,非常常用的类型,那么WEBP呢?它用在什么场合?其实WEBP可以理解为简化版的GIF,很多需要动态封面的地方,只需要后台给一个WEBP的地址,我们用SDWebImage最普通的下载方法进行下载就可以,这并不是多么难的技术,其实非常简单,这里给大家一个例子。

参考文章

1. 文件头

后记

本篇已结束,后面更精彩。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容