之前写了一篇关于PNG/JPEG图片的选取,压缩,上传(下边统一简称前文): http://www.jianshu.com/p/011b5ef47675, 文本依赖前文的UIImage+ZipSizeAndLength分类进行图片的压缩等.
总结一下: 用原生的 UIImagePickerController 选取图片; 用原生的UIImageJPEGRepresentation()和图形上下文对图片进行压缩; 用AFN进行上传操作.
好了,总结完之后进入今天的主题-----GIF图片的选取,压缩,上传;
目录:
一 前言
二 选取图片(重点)
三 获取GIF图片的原始完整二进制数据(重点)
四 压缩图片(重点)
五 上传图片
一 前言
有的同学会疑惑,难道gif图片不能用前文说的PNG/JPEG图片的方法进行选取,压缩和上传吗?
我可以肯定的给你回答---不能(自己亲身敲代码做的test,如有疑问,欢迎留言)
原因如下:
1 前文说的选取图片的代码如下:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
}
对于GIF图片只能选取到第一帧,无法取到所有帧.
2 无法取到GIF所有帧,就不能对图片进行压缩
3 无法取到GIF所有帧,也不能将图片转化成二进制数据
4 没有二进制数据就无法进行上传操作
所以需要换另外一个方法,具体如下.
二 选取图片
首先判断是不是GIF图片,如果是才需要走GIF的逻辑,判断方法有如下两种:
第一种(比较取巧):
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *assetString = [[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString];
if([assetString hasSuffix:@"GIF"]){
//这个图片是GIF图片
} else {
}
}
第二种:利用iOS9.0以后出的 Photos/Photos.h 里边的API进行判断,代码如下:
NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
PHFetchOptions *options = [[PHFetchOptions alloc]init];
PHFetchResult *set = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:options];
//转化NSData
PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
PHImageRequestOptions *option = [PHImageRequestOptions new];
option.resizeMode = PHImageRequestOptionsResizeModeFast;
option.synchronous = YES;
PHAsset *asset = set.firstObject;//本文只是选取一张
[imageManager requestImageDataForAsset:asset
options:option
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if ([dataUTI isEqualToString:(__bridge NSString *)kUTTypeGIF]) {
//这个图片是GIF图片
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypePNG]){
NSLog(@"PNG图片");
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG]){
NSLog(@"JPEG图片");
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG2000]){
NSLog(@"JPEG2000图片");
}else {
}
}];
本文用的是第一种(比较简单,效率高)
三 获取GIF图片的原始完整二进制数据(所有帧)
代码如下:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *assetString = [[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString];
if([assetString hasSuffix:@"GIF"]){
//这个图片是GIF图片
ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc]init];
[assetLibrary assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *re = [asset representationForUTI:(__bridge NSString *)kUTTypeGIF];;
NSUInteger size = (NSUInteger)re.size;
uint8_t *buffer = malloc(size);
NSError *error;
NSUInteger bytes = [re getBytes:buffer fromOffset:0 length:size error:&error];
NSData *data = [NSData dataWithBytes:buffer length:bytes];//这个就是选取的GIF图片的原二进制数据
free(buffer);
} failureBlock:^(NSError *error) {
}];
} else {
}
}
四 压缩图片
利用CoreGraphics/CoreGraphics.h API转换GIF图片并再次转成符合要求的图片的二进制文件:
封装方法如下:
+ (NSData *)zipGIFWithData:(NSData *)data {
if (!data) {
return nil;
}
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
size_t count = CGImageSourceGetCount(source);
UIImage *animatedImage = nil;
NSMutableArray *images = [NSMutableArray array];
NSTimeInterval duration = 0.0f;
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
duration += [self frameDurationAtIndex:i source:source];
UIImage *ima = [UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
ima = [ima zip];
[images addObject:ima];
CGImageRelease(image);
if (!duration) {
duration = (1.0f / 10.0f) * count;
}
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
}
CFRelease(source);
return UIImagePNGRepresentation(animatedImage);
}
将三种得到的gif图片的二进制数据精心压缩:
代码如下:
....
NSData *data = [NSData dataWithBytes:buffer length:bytes];//这个就是选取的GIF图片的原二进制数据
data = [self zipGIFWithData:data];
//上传二进制数据
五 上传图片,与前文上传图片一样
网友评论
如果有需求可以兼容一下,还是不错的
另外在上传的时候也需要注意这种情况
感谢提出宝贵意见
这不是返回的是PNG类型的图吗
[imageManager requestImageDataForAsset:asset
options:option
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if ([dataUTI isEqualToString:(__bridge NSString *)kUTTypeGIF]) {
//这个图片是GIF图片
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypePNG]){
NSLog(@"PNG图片");
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG]){
NSLog(@"JPEG图片");
}else if([dataUTI isEqualToString:(__bridge NSString *)kUTTypeJPEG2000]){
NSLog(@"JPEG2000图片");
}else {
}
}];
block 里的imageData就是PHAsset对应的NSData
第二种主要有个耗时,在纠结用哪个
NSData *imgData = UIImageJPEGRepresentation(self, 0.5);
NSLog(@"压缩后%ld ", imgData.length); // 87564 --例
UIImage *newImage = [UIImage imageWithData: imgData];
NSData *newImgData = UIImageJPEGRepresentation(newImage, 1);
NSLog(@"转换后%ld ", newImgData.length); //2345678 --例
这样看来,UIImageJPEGRepresentation这东西真的能压缩图片吗?
我试过把newImage写入文件,查看文件属性和压缩后大小一致,没问题,再读取这个文件,data还是很大。GIF压缩必然经过每一帧的处理再合成,所以UIImageJPEGRepresentation根本没有任何用处,只能靠调整像素尺寸来减少大小。求解作者,我是不是走入什么误区了?还是有什么其他方法可以使用,研究了两天了,希望能得到您的指点,谢谢。
NSLog(@"压缩后%ld ", imgData.length); // 87564 --例
UIImage *newImage = [UIImage imageWithData: imgData];
NSData *newImgData = UIImageJPEGRepresentation(newImage, 1);
NSLog(@"转换后%ld ", newImgData.length); //2345678 --例
根据你的打印结果,猜测UIImageJPEGRepresentation方法压缩是可逆的,也就是说
NSData *imgData = UIImageJPEGRepresentation(self, 0.5); 这一步确实压缩了,但是UIImageJPEGRepresentation(newImage, 1);调用后可以获取到原图片(可以对比一下原图的大小)
ima = [ima zip];
这些方法请问下怎么写的?