背景:图片方向错误
我们在iOS开发中会碰到一个问题,就是图片在相册里显示是正确的,但获取到后自己展示却是错误的,这是因为手机横屏拍照的时候,图片的朝向信息不是竖直的,我们可以用mac看到图片的如下信息:
通过UIImage的imageOrientation也可以获取到,相册之所以是正确的,是因为相册在显示的时候判断了方向,做了处理。
上网搜,有很多转化图片朝向的方法,与本次的问题无关,这里就不介绍了。
问题出现:
有一天,我们碰到一个需求,图片不仅仅用于展示,还要上传服务器,所以我们按照网上的方法把NSData先转化为UIImage,然后拿到方向后做变换,最终通过UIImageJPEGRepresentation/UIImagePNGRepresentation转化成NSData。看起来一切正常,突然,服务器说,你给我的图片中的GPS和EXIF信息怎么没了?
通过研究,发现从相册中拿到的NSData是包含ExIf和Gps的,但是转化成UIImage后,就丢失了这些信息。。于是就想把原始NSData中的这些信息提起出来,再追加到处理完方向的NSData中。上网搜了很多方法,发现都不是很完美,不是内存泄漏就是到处摘抄的。贴一下最终参考了很多资料并bugfix后的方法吧。
CF对象一定要注意内存管理问题,需要手动释放!
CFRelease空指针会崩溃,一定需要判断下,我们要谨慎细微才能提高自己的代码质量,毕竟客户端线上Crash要比服务端难受很多!另外一个好习惯就是release完就把指针置空: CFRelease(p); p = nil,我这里没做,对自己要求高的同学可以补上。
/**
* @brief 将图片原始数据中的Gps和Exif信息添加到新数据中
* @param origin 从相册获取到的原始数据,包含了exif和gps信息
* @param newData 进行过方向修正的图片数据
*/
- (NSData *)appendMetaDataFromOrigin:(NSData *)origin toNewData:(NSData *)newData {
if (!origin || !newData) {
return newData;
}
CGImageSourceRef originSouceRef = CGImageSourceCreateWithData((__bridge CFDataRef)origin, NULL);
if (!originSouceRef) {
return newData;
}
CFDictionaryRef imageInfoRef = CGImageSourceCopyPropertiesAtIndex(originSouceRef, 0, NULL);
// originRef release
CFRelease(originSouceRef);
if (!imageInfoRef) {
return newData;
}
// 原始的exif/gps信息,getValue不需要release
CFDictionaryRef gpsInfoRef = CFDictionaryGetValue(imageInfoRef, kCGImagePropertyGPSDictionary);
CFDictionaryRef exifInfoRef = CFDictionaryGetValue(imageInfoRef, kCGImagePropertyExifDictionary);
NSMutableData *newImageData = [[NSMutableData alloc] init];
CGImageSourceRef newSourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)newData, NULL);
if (newSourceRef) {
CFDictionaryRef newDataInfoRef = CGImageSourceCopyPropertiesAtIndex(newSourceRef, 0, NULL);
if (newDataInfoRef) {
CFMutableDictionaryRef newDataMutableInfo = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, newDataInfoRef);
if (newDataMutableInfo) {
if (gpsInfoRef) {
CFDictionarySetValue(newDataMutableInfo, kCGImagePropertyGPSDictionary, gpsInfoRef);
}
if (exifInfoRef) {
CFDictionarySetValue(newDataMutableInfo, kCGImagePropertyExifDictionary, exifInfoRef);
}
CFStringRef uti = CGImageSourceGetType(newSourceRef);
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, uti, 1, NULL);
if (destination) {
CGImageDestinationAddImageFromSource(destination, newSourceRef, 0, newDataMutableInfo);
CGImageDestinationFinalize(destination);
CFRelease(destination);
}
CFRelease(newDataMutableInfo);
}
// newDataInfoRef release
CFRelease(newDataInfoRef);
}
// newDataRef release
CFRelease(newSourceRef);
}
// imageInfoRef release
CFRelease(imageInfoRef);
return [newImageData copy];
}