背景:圖片方向錯誤
我們在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];
}