从iPhone相册导入和保存照片的正确方法是什么?

3
我正在将iPhone相册中的照片导入到我的应用程序的文档文件夹中。这是我的代码。
for (int j=0; j<[assetArray count]; j++) {

    ALAsset *assest = [assetArray objectAtIndex:j];
    CGImageRef imageRef = assest.defaultRepresentation.fullResolutionImage;
    UIImage *image = [UIImage imageWithCGImage:imageRef];
    NSData *imageData = UIImageJPEGRepresentation(image);
    [imageData writeToFile:documentsPath atomically:YES];
}

功能正常,但当我尝试导入更高分辨率的图像时,需要更多时间。使用最少的时间进行导入的正确方法是什么?顺便说一下:当我将image转换为NSData时,需要更多时间。


我曾经遇到过同样的问题,但是我通过将资源URL保存在数据库中解决了它。当我需要将图像存储在文档目录中时,我逐个获取资源并将图像存储在文档目录中。可能的原因之一是您正在将资产保存在数组中,另外一个建议是在自动释放池中运行此for循环,这将减少内存问题。 - Leena
2个回答

23

使用fullResolutionImage方法进行此任务存在多个风险。

  1. 对于大型图像,使用fullResolutionImage方法可能会导致内存问题。请注意,照片库(至少在iPad上)也可能包含RAW图像。
  2. 性能不佳,因为从图像文件中,ImageIO首先创建一个CGImageRef,然后将其转换为JPEG格式。这需要时间。
  3. 资产库中还可以包含视频。在这种情况下,fullResolutionImage仅返回视频的预览图像,而不是实际的视频。
  4. 将实际的Asset对象存储起来并不成问题,因为它们在内存中很小。

更好的方法是将图像写入文档目录,可以使用ALAssetsRepresentation的getBytes方法。这种方法在速度和内存方面都应该更加高效。它还可以获取原始图像文件(包括元数据),也可以用于视频。

重写您的示例代码如下:

//reading out the orginal images
    for (int j=0; j<[assetArray count]; j++) {

    ALAssetRepresentation *representation = [[assetArray objectAtIndex:j] defaultRepresentation];
    NSString* filename = [documentPath stringByAppendingPathComponent:[representation filename]];

    [[NSFileManager defaultManager] createFileAtPath:filename contents:nil attributes:nil];
    NSOutputStream *outPutStream = [NSOutputStream outputStreamToFileAtPath:filename append:YES];
    [outPutStream open];

    long long offset = 0;
    long long bytesRead = 0;

    NSError *error;
    uint8_t * buffer = malloc(131072);
    while (offset<[representation size] && [outPutStream hasSpaceAvailable]) {
        bytesRead = [representation getBytes:buffer fromOffset:offset length:131072 error:&error];
        [outPutStream write:buffer maxLength:bytesRead];
        offset = offset+bytesRead;
    }
    [outPutStream close];
    free(buffer);
}


//reading out the fullScreenImages and thumbnails
for (int j=0; j<[assetArray count]; j++) 
{
    @autoreleasepool
    {

        ALAsset *asset = [assetArray objectAtIndex:j];

         NSString *orgFilename = [representation filename];
         NSString *filenameFullScreen = [NSString stringWithFormat:@"%@_fullscreen.png",[orgFilename stringByDeletingPathExtension]]
         NSString* pathFullScreen = [documentPath stringByAppendingPathComponent:filenameFullScreen];

         CGImageRef imageRefFullScreen = [[asset defaultRepresentation] fullScreenImage];
         UIImage *imageFullScreen = [UIImage imageWithCGImage:imageRefFullScreen];
         NSData *imageDataFullScreen = UIImagePNGRepresentation(imageFullScreen);
         [imageDataFullScreen writeToFile:pathFullScreen atomically:YES];

         NSString *filenameThumb = [NSString stringWithFormat:@"%@_thumb.png",[orgFilename stringByDeletingPathExtension]]
         NSString* pathThumb = [documentPath stringByAppendingPathComponent:filenameThumb];

         CGImageRef imageRefThumb = [asset thumbnail];
         UIImage *imageThumb = [UIImage imageWithCGImage:imageRefThumb];
         NSData *imageDataThumb = UIImagePNGRepresentation(imageThumb);
         [imageDataThumb writeToFile:pathThumb atomically:YES];

    }
}

谢谢,它运行得非常好。但是我该如何将缩略图和全屏图像复制到文档文件夹中?或者是否有其他方法可以从文档文件夹中的ALAssetsRepresentation获取这些图像? - Aravindhan
1
我修改了源代码以包括读取fullScreenImage和thumbnail。针对这两个图像,您可以使用之前的方法-因为它们在大小上受限,并且与fullResolutionImage相比仅消耗可预测的内存量。根据您想要做什么,您可以考虑将fullScreenImage保存为JPEG而不是PNG,但这只是一个小的代码更改。 - holtmann
谢谢。我们的应用程序之前一直崩溃,但现在没有了。 - Dan Abramov
这里的documentPath是什么? - Nilesh Kumar

5

我使用ELCImagePicker在从照片库中导入多张照片时遇到了同样的问题。我们无法减少导入所需的时间,但是可以解决崩溃问题。

for (int j=0; j<[assetArray count]; j++) 
{
    @autoreleasepool // This is compiler level feature so will only work on xcode 4.1 or above
    {
         ALAsset *assest = [assetArray objectAtIndex:j];
         CGImageRef imageRef = assest.defaultRepresentation.fullResolutionImage;
         UIImage *image = [UIImage imageWithCGImage:imageRef];
         NSData *imageData = UIImagePNGRepresentation(image);
         [imageData writeToFile:documentsPath atomically:YES];
    }
}

如果可能的话,尽量只在assetArray中存储AssetURL而不是整个ALAsset对象,并从URL一次创建一个ALAsset,这样可能有助于减少内存消耗。在这种情况下,您需要使用块。
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
    CGImageRef iref = [[myasset defaultRepresentation] fullResolutionImage];
    if (iref) //You have image so use it 
    {
    }
};

ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)
{
    NSLog(@"Can't get image - %@",[myerror localizedDescription]);
};

ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:imageURL resultBlock:resultblock failureBlock:failureblock];

我们可以缩短时间。我看到了这个应用程序http://itunes.apple.com/us/app/lock-photo-video-note-audio/id448033053?mt=8,它的导入速度非常快。 - Aravindhan
在你的代码中将照片导入设备而非模拟器需要多长时间?你能否请提供更多详细信息,例如照片数量和分辨率? - Janak Nirmal
自动释放池修复了崩溃问题...谢谢..但我不知道如何减少时间.... - Aravindhan
我已经测试了上述代码的导入时间,导入一个1.7 MB大小的文件大约需要2到2.3秒。对于大小分别为1.7 MB和0.8 MB的两个文件,最多需要4秒钟。 - Janak Nirmal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接