在进行iOS开发时,经常会遇到这种情况:
- 常常想不下载图片,根据网络图片URL获取图片的尺寸。根据得到尺寸或者比例显示占位图片与轮廓预览区域。当完全下载下来图片后,替换上去。
- TableView显示的时候要动态的设置Cell的高度,因为大量的图片不会一口气下载完成,所以想先拿到图片的高度把Cell的高度提前设置好。
- 根据图片尺寸或比例动态创建控件
方法
1.服务端返回的JSON等等数据的时候不仅仅返回一个URL,额外返回一个字段描述图片大小。
1 2 3 4 5 6 |
{ "object": { "image_url": "images/pic.jpg", "image_size": "222x255" } } |
2.返回图片URL时拼接该图片的宽高
比如http://xxx.com/images/pic_215x500.jpg,http://xxx.com/images/pic.jpg@215x500 ,http://xxx.com/images/pic_215_500.jpg,格式与服务器端约定好即可。
最好是上传图片的时候直接把尺寸拼接将要存储的图片文件,这样正常访问图片的url解析尺寸就行了。
3.HTTP响应头中写入图片的宽高
4.完全下载下来图片后获的宽高
这种方式,耗费性能与流量。完全不建议。是有没办法的办法。
5.图片数据头中写入图片的宽高
此方法需要服务器支持Range分段请求,整个图片虽然很大,可能只需要很少的字节就能得到图片的大小
curl判断服务器是否支持Range请求
mac的终端下执行:
1 |
curl -i --rang 0-9 https://sqimg.qq.com/qq_product_operations/im/qqlogo/imlogo_b.png |
返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
HTTP/1.1 206 Partial Content Server: Qnginx/1.3.2 Date: Tue, 28 Mar 2017 05:19:13 GMT Content-Type: image/png Content-Length: 10 Connection: keep-alive Cache-Control: max-age=2592000 Expires: Thu, 27 Apr 2017 05:19:13 GMT Last-Modified: Tue, 29 Mar 2016 11:07:42 GMT Content-Range: bytes 0-9/2148 Access-Control-Allow-Origin: * X-Cache-Lookup: Hit From Disktank X-NWS-LOG-UUID: bd8d7f0d-010c-4482-b535-6abefd2e893e ?PNG |
输出结果 Accept-Ranges: bytes 或者statuscode等于206,说明服务器支持按字节下载。我们看到标准的返回中Content-Type就可以判断图片类型了。
iOS代码检测
1 2 3 4 5 6 7 8 9 10 11 12 |
+(BOOL)checkSupportResume:(NSURL*)url{ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"HEAD";//0-9 range //[request setValue:@"bytes=0-9" forHTTPHeaderField:@"Range"]; NSURLResponse *response; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]; NSHTTPURLResponse *resp = (NSHTTPURLResponse*)response; // NSLog(@"resp%@", [resp allHeaderFields]); // NSLog(@"文件长度-%lld",response.expectedContentLength); BOOL support = resp.statusCode ==206?YES:NO; return support; } |
iOS从图片数据头请求尺寸
常见图片的range
PngRange = @"bytes=16-23";
JPGRange = @"bytes=0-209";
GIFRange = @"bytes=6-9";
请求
1 2 3 4 5 |
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:imgURL]; [request setValue:PngRange forHTTPHeaderField:@"Range"]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { //data 即是图片的数据头 }]; |
尺寸解析
从数据头中解析图片的尺寸大同小异,也许每个人的方法具体算法不同,但是核心还是一样的。当然这么多代码肯定不是我写的。来自互联网。
PNG,png尺寸在16-23字节中,所以请求的时候只需要请求8字节即可解析出具体尺寸:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
+ (CGSize)PNGImageSizeWithRangeHeader:(NSData *)data{ int w1 = 0, w2 = 0, w3 = 0, w4 = 0; [data getBytes:&w1 range:NSMakeRange(0, 1)]; [data getBytes:&w2 range:NSMakeRange(1, 1)]; [data getBytes:&w3 range:NSMakeRange(2, 1)]; [data getBytes:&w4 range:NSMakeRange(3, 1)]; int w = (w1 << 24) + (w2 << 16) + (w3 << 8) + w4; int h1 = 0, h2 = 0, h3 = 0, h4 = 0; [data getBytes:&h1 range:NSMakeRange(4, 1)]; [data getBytes:&h2 range:NSMakeRange(5, 1)]; [data getBytes:&h3 range:NSMakeRange(6, 1)]; [data getBytes:&h4 range:NSMakeRange(7, 1)]; int h = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4; return CGSizeMake(w, h); } |
GIF
1 2 3 4 5 6 7 8 9 10 11 12 |
+(CGSize)GIFImageSizeWithRangeHeader:(NSData *)data{ short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0, 1)]; [data getBytes:&w2 range:NSMakeRange(1, 1)]; short w = w1 + (w2 << 8); short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(2, 1)]; [data getBytes:&h2 range:NSMakeRange(3, 1)]; short h = h1 + (h2 << 8); return CGSizeMake(w, h); } |
JPG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
+(CGSize)JPGImageSizeWithRangeHeader:(NSData *)data{ if ([data length] <= 0x58) { return CGSizeZero; } if ([data length] < 210) {// 肯定只有一个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } else { short word = 0x0; [data getBytes:&word range:NSMakeRange(0x15, 0x1)]; if (word == 0xdb) { [data getBytes:&word range:NSMakeRange(0x5a, 0x1)]; if (word == 0xdb) {// 两个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0xa5, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0xa6, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0xa3, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0xa4, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } else {// 一个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } } else { return CGSizeZero; } } } |
总结
从图片头中获取尺寸并不是一个高效的方法,耗费服务器性能,增加服务端负担。
其实如果是自己服务器的图片,采用url附带尺寸是非常好的选择。最好是上传图片的时候直接把尺寸拼接将要存储的图片文件名中。
从图片数据头请求尺寸Demo
https://github.com/shaojiankui/UIImage-RemoteSize
转载请注明:天狐博客 » iOS开发之预读取网络图片尺寸