使用ImageIO上傳保留EXIF資訊的圖檔
相信大多數項目中需要上傳圖檔,但是從相機中取到的圖檔再上傳到伺服器,一般的項目就是直接用
NSData *data = UIImageJPEGRepresentation(image, 1)
把圖檔壓縮轉換成DATA進行上傳。然而在轉成UIImage的時候,會把圖檔的EXIF一些資訊自動隐藏,是以别人拿到你的圖檔别人是看不到這些資訊的。是以我就在網上找到了以下的方法,主要借助ImageIO架構,先寫入本地再進行上傳。
stack overflow原文
ALAssetRepresentation *image_Representation = [asset defaultRepresentation];
// create a buffer to hold image data
uint8_t *buffer = (Byte *)malloc(image_Representation.size);
NSUInteger length = [image_Representation getBytes:buffer fromOffset: length:image_Representation.size error:nil];
if (length != ) {
// buffer -> NSData object; free buffer afterwards
NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:image_Representation.size freeWhenDone:YES];
// identify image type (jpeg, png, RAW file, ...) using UTI hint
float compression = ;
int orientation = ;
NSDictionary *sourceOptionsDict = @{(__bridge id)kCGImageSourceTypeIdentifierHint:[image_Representation UTI],
(__bridge id)kCGImageDestinationLossyCompressionQuality: (__bridge id)CFNumberCreate(NULL, kCFNumberFloatType, &compression),
(__bridge id)kCGImagePropertyOrientation:(__bridge id)CFNumberCreate(NULL, kCFNumberIntType, &orientation),
(__bridge id)kCGImagePropertyHasAlpha:(__bridge id)kCFBooleanTrue};
// create CGImageSource with NSData
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)adata, (__bridge CFDictionaryRef)sourceOptionsDict);
// get imagePropertiesDictionary
CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(sourceRef,, NULL);
// get exif data
CFDictionaryRef exif = (CFDictionaryRef)CFDictionaryGetValue(imagePropertiesDictionary, kCGImagePropertyExifDictionary);
NSDictionary *exif_dict = (__bridge NSDictionary*)exif;
NSLog(@"exif_dict: %@",exif_dict);
// save image WITH meta data
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, , imagePropertiesDictionary);
//存入本地
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:];
NSURL *fileURL = nil;
if (![[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"] isEqualToString:@"public.tiff"]) {
fileURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.%@",
documentsDirectory,
@"myimage",
[[[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"] componentsSeparatedByString:@"."] objectAtIndex:]
]];
CGImageDestinationRef dr = CGImageDestinationCreateWithURL ((__bridge CFURLRef)fileURL,
(__bridge CFStringRef)[sourceOptionsDict objectForKey:@"kCGImageSourceTypeIdentifierHint"],
,
NULL
);
CGImageDestinationAddImage(dr, imageRef, imagePropertiesDictionary);
CGImageDestinationFinalize(dr);
CFRelease(dr);
NSData *imageData = [NSData dataWithContentsOfURL:fileURL];
// NSLog(@"%lu",(unsigned long)imageData.length);
[blockSelf.selectedPhotos addObject:imageData];
}else {
NSLog(@"no valid kCGImageSourceTypeIdentifierHint found …");
}
// clean up
CFRelease(imageRef);
CFRelease(imagePropertiesDictionary);
CFRelease(sourceRef);
}else {
NSLog(@"image_representation buffer length == 0");
}
然而你會發現這裡比原文多了一點。
原圖太大,上傳辣麼大的圖,慢,費流量,是以需要就原圖進行壓縮。如果想要對原圖進行壓縮,就需要在
sourceOptionsDict
這個字典中添加
kCGImageDestinationLossyCompressionQuality
這個KEY對應的VALUE為
CFNumber
此值0.0~1.0.如此添加之後,我以為就OK了,然而并沒有什麼卵用,原圖是多大,壓縮後還是多大。思前想後,我覺得一定是一些屬性對壓縮産生了沖突,于是我就查閱了蘋果官方文檔,發現沒什麼大的改變,也就是字典那的一些轉換。那就用官方的試試…
ALAssetRepresentation *image_Representation = [asset defaultRepresentation];
float compression = ; // 壓縮比例
int orientation = ; // 方向
CFStringRef myKeys[];
CFTypeRef myValues[];
CFDictionaryRef myOptions = NULL;
myKeys[] = kCGImagePropertyOrientation;
myValues[] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
myKeys[] = kCGImagePropertyHasAlpha;
myValues[] = kCFBooleanTrue;
myKeys[] = kCGImageDestinationLossyCompressionQuality;
myValues[] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
myKeys[] = kCGImageSourceTypeIdentifierHint;
myValues[] = (__bridge CFTypeRef)([image_Representation UTI]);
myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, ,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
//建立DATA
uint8_t *buffer = (Byte *)malloc(image_Representation.size);
NSUInteger length = [image_Representation getBytes:buffer fromOffset: length:image_Representation.size error:nil];
NSData *adata = [[NSData alloc] initWithBytesNoCopy:buffer length:image_Representation.size freeWhenDone:YES];
/*********建構CGImageRef*******/
//CGImageSourceCreateWithData
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)adata, myOptions);
CFDictionaryRef imagePropertiesDictionary = CGImageSourceCopyPropertiesAtIndex(sourceRef,, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, , imagePropertiesDictionary);
/*********建構檔案URL*******/
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:];
NSString *imageType = [(__bridge NSDictionary *)myOptions objectForKey:@"kCGImageSourceTypeIdentifierHint"];
NSURL *fileURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@.%@",
documentsDirectory,
@"myimage",
[[imageType componentsSeparatedByString:@"."] objectAtIndex:]
]];
[blockSelf writeCGImage:imageRef toURL:fileURL withType:(__bridge CFStringRef)(imageType) andOptions:myOptions];
- (void)writeCGImage: (CGImageRef) image toURL: (NSURL*) url withType: (CFStringRef) imageType andOptions: (CFDictionaryRef) options
{
CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef)url, imageType, , NULL);//此處NULL不能為nil,否則圖檔為空
CGImageDestinationAddImage(myImageDest, image, options);
CGImageDestinationFinalize(myImageDest);
CFRelease(myImageDest);
}
如此改變之後,就成功的壓縮了圖檔,上傳的時候直接取到圖檔轉成Data然後進行上傳。
然而對比了使用ImageIO與使用UIImage的方法之後會發現,還是使用
UIImageJPEGRepresentation
進行壓縮得到的圖檔更小一些,這需要根據項目而定采用哪種方式。