最新消息:iOS编程开发交流群(6906921) ,Mac.Cocoa开发交流群(7758675) 欢迎iOS/macOS开发编程爱好及学习者加入!

iOS开发之预读取网络图片尺寸

iOS 天狐 17103浏览 0评论

在进行iOS开发时,经常会遇到这种情况:

  • 常常想不下载图片,根据网络图片URL获取图片的尺寸。根据得到尺寸或者比例显示占位图片与轮廓预览区域。当完全下载下来图片后,替换上去。
  • TableView显示的时候要动态的设置Cell的高度,因为大量的图片不会一口气下载完成,所以想先拿到图片的高度把Cell的高度提前设置好。
  • 根据图片尺寸或比例动态创建控件

方法

1.服务端返回的JSON等等数据的时候不仅仅返回一个URL,额外返回一个字段描述图片大小。

{
    "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的终端下执行:

curl -i --rang 0-9 https://sqimg.qq.com/qq_product_operations/im/qqlogo/imlogo_b.png

返回

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代码检测

+(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";

请求

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字节即可解析出具体尺寸:

+ (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

+(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

+(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开发之预读取网络图片尺寸

微信 OR 支付宝 扫描二维码
为天狐 打赏
非常感谢你的支持,哥会继续努力!
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址