一张小图.png

前言

客户端研发时,有时会有这样的需求,需要根据图片链接地址获取图片的宽高来进行界面排版。

一般比较正规的做法,是服务端在返回数据时将图片的信息属性一起带回来,这也符合轻客户端设计规范。但是现实不是理想,有时就是会出现服务端没有返回,你却要知道图片宽高,所以本文,针对通过 URL 来获取图片宽高进行简单的介绍。

传统获取图片宽高方案:

最为常见也是最慢的一种方案,通过 URL 下载图片,得到图片数据后获取图片宽高。

这种方式 iOS 下有很多实现方案,可以使用三方工具进行图片下载,也可以直接自己写,该方法思想就是通过下载整个图片然后得到完整的图片数据信息,解析数据,再从中得到图片的宽高。 比如本文使用 CGImageSource 来通过 URL 图片信息,从而得到图片的宽高。代码如下:

// 获取图像属性

CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);

返回的 imageProperties 就已经从整个图片数据中获取到需要的信息了,我们只需要从中通过 KEY 来取出需要的值。

// 获取宽高

CFNumberRef widthNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);

CFNumberRef heightNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);

改进方案:

在传统方案里,获取图片的宽高的时候,下载了整个图片数据,但这里我们的需求只是获取图片的宽高信息,此时还不需要全部的图片信息,下载完整的图片数据,导致需要获取的数据量更大,从而增加的宽高获取到的时间,减少了我们的流畅性。

如果我们通过 URL 只下载我们需要的图片中的某些信息的话(比如宽高信息),就能减少此次请求的传输的数据量,从而加快信息的获取。那如何实现呢?

在图片数据中,不管什么格式,在表示该图片的数据中都有一段数据块表示着这个图片的描述信息,其中就包含着该图片的宽高大小。所以知道这点,我们只要通过 URL 请求获取该部分段信息,就可以从中解析出我们需要的图片宽高。

让我们先看下 PNG 图片的数据格式的大概模样,如下图:

PNG数据头数据图

图中我们只需要关注 PNG Signature 与标红的 WIDTH HEIGHT 段,PNG Signature标志着该图片是一张 PNG 图,知道它是 PNG 图数据后,如果图片数据的开头与这张图中一致,则代表该图片是一张 PNG 图。

在数据的固定位置处,即 WIDTH HEIGHT 所在的字节位置里存放的就是该图片的宽高信息,所以我们只需要从该处取出所存数据就知道图片宽高了。

同理针对其他格式的图片也是一样的,只是他们中数据的段格式以及位置有些不同,但都存在着这样一个数据段表示着图片的描述信息。(这里并不对所有的图片格式进行介绍,这里了解资料)

下面是 GIF 图的文件头格式图:

GIF头格式

代码实现:

方式1:通过设置 HTTP 请求头 Range 字段来获取数据的某位置段数据;

比如,此时有一张 PNG 图的链接地址,想要知道其宽高,代码如下:

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

[request setValue:@"bytes=16-23" forHTTPHeaderField:@"Range"];

NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil

error:nil];

CGSize size = CGSizeZero;

if (data.length >= 8) {

int w = 0, h = 0;

[data getBytes:&w range:NSMakeRange(0, 4)];

[data getBytes:&h range:NSMakeRange(4, 4)];

w = CFSwapInt32BigToHost(w);

h = CFSwapInt32BigToHost(h);

size = CGSizeMake(w, h);

}

在16-23字节位置处,前4字节代表着宽,后4字节代表着高,由此我们就完成了图片的宽高获取,相对于传统方式,不管图片真实大小多大,我们只下载了仅仅 8 字节的数据,无疑加快了速度和节省了流量,其他格式图代码可见文件。

方式2:直接下载,在网络回调中解析数据,得到足够数据后,解析出宽高,提前停止请求。

在第一种方式中,虽然速度很快但存在一个问题,下载前必须先知道图片宽高数据存储位置,对于 PNG 和 GIF 图片来说是没有问题,但在 JPG 格式图时,由于其数据段并不是在文件的头部,也不再固定的位置,可能在中间的任何一段地方,所以通过提前指定请求头 的 Range 范围是无法有效获取到信息的,此时我们只能通过一边下载图片数据,一边在解析得到的数据,如果检测到了图片的描述信息段,则开始解析,解析成功后提前结束网络请求,这样在速度和流量方面相对于传统的依然是有一定的提升。下图为 JPG 图数据格式:

image

其中 FFCO段为描述段信息开头,我们在代码中通过While 来在一个个数据段中寻找该描述段,找到了它就找到了宽高。

代码如下:

- (CGSize)fetchHWFromJPGData:(NSData *)data {

CGSize size = CGSizeZero;

// FF D8 FF E0 (XX XX 这两字节为长度) ('JF' 'TF' 转为ascll码值)

UInt8 word0 = 0x0, word1 = 0x0, word2 = 0x0, word3 = 0x0;

[data getBytes:&word0 range:NSMakeRange(0, 1)];

[data getBytes:&word1 range:NSMakeRange(1, 1)];

[data getBytes:&word2 range:NSMakeRange(2, 1)];

[data getBytes:&word3 range:NSMakeRange(3, 1)];

if (word0 == 0xFF && word1 == 0xD8 && word2 == 0xFF && word3 == 0xE0) {

UInt8 c0 = 0, c1 = 0, c2 = 0, c3 = 0;

[data getBytes:&c0 range:NSMakeRange(6, 1)];

[data getBytes:&c1 range:NSMakeRange(7, 1)];

[data getBytes:&c2 range:NSMakeRange(8, 1)];

[data getBytes:&c3 range:NSMakeRange(9, 1)];

if (c0 == 'J' && c1 == 'F' && c2 == 'I' && c3 == 'F') {

UInt16 block_length = 0;

[data getBytes:&block_length range:NSMakeRange(4, 2)];

block_length = XCSSwapWebIntToInt16(block_length);

int i = 4;

do {

i += block_length;

if (i > data.length) {

break;

}

UInt8 aW = 0x0;

[data getBytes:&aW range:NSMakeRange(i, 1)];

if (aW != 0xFF) {

break;

}

UInt8 ca = 0x0;

[data getBytes:&ca range:NSMakeRange(i+1, 1)];

if (ca >= 0xC0 && ca<= 0xC3) {

/**

图片信息段 FF CO (xx xx 该段长度) XX(抛弃字节,不用) (HH HH 高度) (WW WW 宽度) ...后面是其他信息。

这里宽高段和 png gif 等都不一样,jpg 是高在前

*/

UInt16 w = 0, h = 0;

[data getBytes:&h range:NSMakeRange(i + 5, 2)];

[data getBytes:&w range:NSMakeRange(i + 7, 2)];

w = XCSSwapWebIntToInt16(w);

h = XCSSwapWebIntToInt16(h);

size = CGSizeMake(w, h);

break;

}else {

i += 2;

[data getBytes:&block_length range:NSMakeRange(i, 2)];

block_length = XCSSwapWebIntToInt16(block_length);

}

} while (i < data.length);

}

}

return size;

}

更多详情代码,可见DEMO。

最后结果代码

XCSImagePrefetcher 通过传入 URL 来获取图片的宽高。

@interface XCSImagePrefetcher : NSObject

@property (nonatomic, strong, readonly) NSMutableData *downloadData;

@property (nonatomic, strong, readonly) NSURL *imageUrl;

- (instancetype)init NS_UNAVAILABLE;

- (instancetype)initWithUrl:(NSURL *)url NS_DESIGNATED_INITIALIZER;

- (CGSize)fetchImageSize;

@end

使用方式如下:

XCSImagePrefetcher *fetcher = [[XCSImagePrefetcher alloc] initWithUrl:[NSURL URLWithString:JPG_IMG_URL]];

NSLog(@"%@", NSStringFromCGSize([fetcher fetchImageSize]));

更多测试代码,可见DEMO。

总结

在数据的提取过程中,需要注意大小端问题导致的数据解析出来不对(相关知识);

即使通过这种方式进行优化,获取图片大小问题仍然因为需要发送网络请求而变的速度不够稳定,所以真正的解决方案,还是需要服务端配合添加上图片数据宽高的记录;

实际应用中需要和缓存配合来达到最佳效果。

相关资料

android 通过图片url获取宽高_通过 URL 获取图片宽高优化相关推荐

  1. java获取cpu使用率_再一次生产 CPU 高负载排查实践

    前言 前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨. 其实早在去年我也处理过类似的问题,并记录下来:<一次生产 CPU 1 ...

  2. qt获取cpu使用率_又一次生产 CPU 高负载排查实践

    以下文章来源于crossoverJie ,作者crossoverJie 前言 前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨. 其 ...

  3. 三星android怎么获取root,三星S8如何获取ROOT权限_三星S8ROOT获取教程

    三星S8如何获取ROOT权限,三星S8ROOT获取教程.今天在这里分享一下咱们的三星Galaxy S8+手机的root教程,这个root教程也是采用卡刷的方式进行root,操作上也简单,因为之前给大家 ...

  4. Axure 点图片外区域即隐藏_好用的图片处理软件JixiPix Hand Tint Pro

    小编相信不少Mac用户都会用到这款JixiPix Hand Tint Pro for Mac的工具的,它可是一款非常实用的图片处理软件哦,可以为Mac用户带来丰富的图像效果和过滤器等服务,并且在这款J ...

  5. sqlmap 获取mysql密码_利用sqlmap 获取管理员账号密码

    首先,先简单介绍下sqlmap 简介: sqlmap是一种开源的 支持的数据库:MySQL,Oracle, PostgreSQL, Microsoft SQL Server, Microsoft Ac ...

  6. python获取url的json数据_通过url获取json数据并在python中使用(simplejson)

    我想这肯定有一个简单的答案,但我正在努力:我想获取一个url(输出json)并在python中的一个可用字典中获取数据.我被困在最后一步.>>> import urllib2 > ...

  7. java根据url获取pdf流_从URL获取动态创建的PDF

    我需要获取从aspx站点生成的pdf文件 . Backstory : 我想从一个网站获得schedueles,我已成功获取包含参数的scheduele的url . 如果您关注该网址,您将看到一个PDF ...

  8. python如何将图片打包进exe里_用python将图片切分为九宫格 并打包成exe可执行文件(附源码)...

    前言 经常在朋友圈或者微博看到九宫格显示的图片,虽然是九张图片,但是这是一张图片经过切割而成的,显示效果很震撼.今天,我们就用python将图片切分为九宫格,并打包成exe可执行文件,就算不需要代码也 ...

  9. python获取摄像头数据_用Python获取摄像头并实时控制人脸 !

    实现流程从摄像头获取视频流,并转换为一帧一帧的图像,然后将图像信息传递给opencv这个工具库处理,返回灰度图像(就像你使用本地静态图片一样) 程序启动后,根据监听器信息,使用一个while循环,不断 ...

最新文章

  1. 在HYPER-V中利用差异磁盘和SYSPREP技术安装多个WINDOWS 2008
  2. 2.1.2 数据通信基础知识
  3. V3S拍照上传又拍云bug排查过程
  4. python 设计模式 观察者_设计模式Python实现-观察者模式
  5. Android Gradle 批量修改生成的apk文件名
  6. 机器学习之线性回归 损失函数、代价函数、目标函数
  7. QT5_数据类型转化
  8. 正则表达式基础知识,持续更新…
  9. 【MySQL】Linux下登录mysql时忘记密码了怎么办?
  10. pygame精灵组有哪些方法_利用 pygame 开发一款游戏:「跳跳兔」(六)
  11. Java语言中:switch语句经典习题
  12. Linux用户和组账户管理
  13. office 2003 兼容包 (兼容 office 2007 office 2010)
  14. 基于3DMM的三维人脸重建技术总结
  15. 问题:为什么我们计算的GDP增长率跟统计局公布的不一样
  16. pdf加页码java_Java 添加页码到PDF文档
  17. Vue2知识点 - RT
  18. 网络 路由器基本协议配置
  19. 02- web UI测试与UI Check List
  20. iOS生成四位随机数

热门文章

  1. ipvs学习笔记(二)
  2. 康柏川(帮别人名字作诗)
  3. bootstrap4 后台管理模板_开源的后台管理模板
  4. 学习笔记(2):uni-app实战社区交友类app开发-引入css动画库
  5. 二十三种设计模式详解
  6. ajax传输文件大小有没有限制_巧改文件扩展名,1秒解除微信传输文件大小限制!...
  7. aspectj 注解
  8. 判断整除(动态规划,递推)
  9. 人工智能切入垂直领域 风口已至?
  10. 基于Python的接口自动化测试框架