天天看點

使用ImageIO保留EXIF資訊的圖檔

使用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

進行壓縮得到的圖檔更小一些,這需要根據項目而定采用哪種方式。