以最有效,最快捷的方式下载大量图像

时间:2016-12-07 05:37:28

标签: ios objective-c afnetworking-2

在我的应用中,我需要下载约6,000张图片。我意识到这很多,但需要它。

目前我正在使用以下内容:

NSArray *photos = @[hugeAmountOfPhotoObjects];
for (ZSSPhoto *photo in photos) {
    [self downloadImageWithURL:photo.mobileURL progress:^(double progress) {

    } completion:^(UIImage *image) {

        // Save the image

    } failure:^(NSError *error) {

    }];
}

...

- (void)downloadImageWithURL:(NSURL *)url progress:(void (^)(double progress))progress completion:(void (^)(UIImage *image))completion failure:(void (^)(NSError *error))failure {

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setTimeoutInterval:600];
    self.operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
    AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
    [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        completion(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [self processOperation:operation error:error failure:failure];
    }];
    [requestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
        double percentDone = (double)totalBytesRead / (double)totalBytesExpectedToRead;
        progress(percentDone);
    }];
    [self.operationQueue addOperation:requestOperation];

}

这里的问题是使用此方法下载需要花费很长时间,并且由于内存使用率过高,我的一些用户报告崩溃。

有没有更好的方法可以用来下载如此大量的图像文件?

2 个答案:

答案 0 :(得分:0)

你可以稍微递归地试试

NSMutableArray *undownloaded;

- (void) startDownload {

    undownloaded = [photos mutableCopy]; //get a list of the undownloaded images

    for(int i = 0; i < 3;i++) //download 3 at a time
        [self downloadImage];
}


- (void) downloadImage {

    if(undownloaded.count > 0){

        ZSSPhoto *photo = undownloaded.firstObject;
        [undownloaded removeObjectAtIndex:0];

        [self downloadImageWithURL:photo.mobileURL progress:^(double progress) {

        } completion:^(UIImage *image) {

            // Save the image

            [self downloadImage];

        } failure:^(NSError *error) {

            [self downloadImage];
            //[undownloaded addObject:photo]; //insert photo back into the array maybe to retry? warning, could cause infinite loop without some extra logic, maybe the object can keep a fail count itself

        }];
    }
}

警告:未经测试的代码,可能需要一些调整

答案 1 :(得分:0)

速度问题可以解决(速度会增加,但可能仍然很慢)使用多线程,同时下载所有图像而不是每次下载一次。但是,内存问题有点复杂。

ARC将在所有图像完成后释放所有图像,但在此之前,您将在设备内存中拥有6,000张图像。您可以优化图像,降低分辨率或按步骤下载它们,例如Google Images(您下载的图像最初会显示,然后当用户向下滚动时,您将图像加载到新的可见区域;下载图像只有在他们需要的时候)。

考虑到您正在下载足够的图像以解决内存问题,如果您下载了所有这些内容,则可能会占用用户设备的大量空间,以及“步骤”。解决方案也可以解决这个问题。

现在,让我们假设您必须同时下载所有这些并且空间不是问题:我想如果您将downloadImageWithURL:progress:方法放在并发队列中,那么图像只是在保存之后才能从记忆中解脱出来(这只是一个假设)。将其添加到您的代码中:

dispatch_queue_t defaultPriorityQueueWithName(const char* name)
{
    dispatch_queue_t dispatchQueue = dispatch_queue_create(name, DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t priorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_set_target_queue(dispatchQueue, priorityQueue);
    return dispatchQueue;
}

将代码更改为:

dispatch_queue_t threadItemLoadImage = defaultPriorityQueueWithName("DownloadingImages");
NSArray *photos = @[hugeAmountOfPhotoObjects];
for (ZSSPhoto *photo in photos) 
{
    dispatch_async(threadItemLoadImage, ^
    {
        [self downloadImageWithURL:photo.mobileURL progress:^(double progress) {

        } completion:^(UIImage *image) {

            // Save the image

        } failure:^(NSError *error) {

        }];
    });
}

如果更新了某些视图,则需要删除setDownloadProgressBlock:,因为它们会同时下载。另外,警告:totalBytesExpectedToRead并不总是会在第一次被正确检索,包含0,这可能会使您的应用程序崩溃,在某些罕见情况下除以零。在将来的情况下,当您需要使用setDownloadProgressBlock:时,请在进行此除法之前检查totalBytesExpectedToRead值。