如何使用 AFNetworking 从一系列 URL 下载和存储图像?

Posted

技术标签:

【中文标题】如何使用 AFNetworking 从一系列 URL 下载和存储图像?【英文标题】:How to download and store images from an array of URLs using AFNetworking? 【发布时间】:2013-11-18 17:27:50 【问题描述】:

我正在使用"UIImageView+AFNetworking.h",我的目标是获取一个名为imageURLArray 的URL NSArray,下载所有图片,将它们放在带有页面控件的滚动视图中。

为此,我在以下循环中一次下载一张图片,并有一个先前分配/初始化的可变数组 downloadImageArray 我存储下载的图片:

for (int i = 0; i < [imageURLArray count]; i++)
        NSURL *testURL = [imageURLArray objectAtIndex:i];
        NSURLRequest *testURLRequest = [[NSURLRequest alloc] initWithURL:testURL];
        [placeHolderImageView setImageWithURLRequest:testURLRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) 
            NSLog(@"success");
            NSLog(@"The image is %@", image);
            [downloadedImageArray addObject:image]; //! the exception is thrown here - it says the object is NULL
         failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) 
            NSLog(@"failed to download");
        ];
    
NSLog(@"Count of images is %i", [downloadedImageArray count]) //this always returns "0"

作为我将对象添加到downloadedImageArray 的行中的注释,我得到添加到可变数组的对象为 NULL 的异常。我怀疑这是因为在下一个 URL 到位之前图像尚未完成下载,并被要求再次下载。另外,在循环之后,当我得到downloadedImageArray 的计数时,计数返回0。

我的问题是:

    如何在循环到下一个请求以下载下一个图像之前确保一个图像已完成下载? 作为一种设计选择,在将它们添加到我的滚动视图之前先下载所有图像是否有意义(每个图像的宽度设置为固定),还是在滚动到它们时下载更有意义?如果是这样,我该如何实施?大多数情况下,imageURLArray 的 URL 集不超过 4 组。

谢谢!

【问题讨论】:

你为什么要把每张图片都分配给同一个图片视图? 我使用了placeHolderImageView,因为我需要一个imageView 来调用setImageWithUrlRequest——至少这是我的理解。 如果您的目标是下载图像只是为了将它们存储在数组中,那么您为什么要使用图像视图呢?只需使用 AFNetworking 下载图像。它有更直接的 API 用于从远程 URL 下载文件。 感谢您的建议,我的目标确实是下载UIImages(我目前正在使用AFNetworking)。但是,我不知道AFNetworking 中有任何方法不需要 UIImageView 才能工作,因为它们都是[UIImageView setImageWithURL NSURL] 的变体。您能否指出您在 API 中指的是哪种方法? Download a file / image with AFNetworking in ios?的可能重复 【参考方案1】:

如果您使用的是最新版本的 AFNetworking,要下载图像,您可以这样做:

NSURLRequest* request = [...]
AFHTTPRequestOperation* op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFImageResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation* operation, id responseObject)... failure:^(AFHTTPRequestOperation* operation, NSError* error) ...];

[op start];

因此,如果您想遍历一组 URL 并从每个 URL 下载一张图片,您可以这样做(并且警告一句:我还没有测试过):

NSMutableArray* images = [NSMutableArray arrayWithCapacity:imageURLArray.count];
for(int i = 0; i < imageURLArray.count; i++)
    [images addObject:[NSNull null]];
for(int i = 0; i < imageURLArray.count; i++)

    NSURL* url = imageURLArray[i];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    AFHTTPRequestOperation* op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    op.responseSerializer = [AFImageResponseSerializer serializer];
    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation* operation, id responseObject)
        UIImage* image = responseObject;
        [images replaceObjectAtIndex:i withObject:image];
     failure:^(AFHTTPRequestOperation* operation, NSError* error) ...];

    [op start];

你用NSNulls 填充图像数组,因为不同索引的图像可能在不同的时间加载,如果说索引 3 的图像在索引 2 的图像之前加载,你会得到一个越界异常,如果你尝试将其插入到正确的索引处。

这些操作完成后,images 数组中的每张图片都将对应imageURLArray 中相同索引处的 URL。

话虽如此,但大多数时候最好在需要时下载所需的图像。所以我会让你的视图控制器成为你的图像滚动视图的代表,然后当滚动发生时,找出出现了哪些UIImageViews,并在它们上调用setImageWithURL。但是,如果这太难了,并且您没有大量非常大的图像,那么上面应该没问题。

【讨论】:

谢谢 - 做到了!添加空图像来填充数组绝对是我需要做的。并感谢有关仅下载我需要的图像的建议。我进行了重构以考虑到这一点。【参考方案2】:

回答。 1 我们可以使用 NSRunLoop 来等待进程完成试试这个

for (int i = 0; i < [imageURLArray count]; i++)

NSURL *testURL = [imageURLArray objectAtIndex:i];
NSURLRequest *testURLRequest = [[NSURLRequest alloc] initWithURL:testURL];
[placeHolderImageView setImageWithURLRequest:testURLRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) 
    NSLog(@"success");
    NSLog(@"The image is %@", image);

    while (!image)
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

    [downloadedImageArray addObject:image]; //! the exception is thrown here - it says the object is NULL
 failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) 
    NSLog(@"failed to download");
];

NSLog(@"Count of images is %i", [downloadedImageArray count]);

现在图像在未下载之前不会添加到数组中。

回答。 2 按照此过程显示图像

一个。下载图片

b.添加到数组

c。添加到滚动条

执行此操作,直到未下载所有图像。

【讨论】:

阅读问题下的 cmets。无需使用图像视图并将图像加载到其中。 我只是修改它的代码以防止数组添加零图像。这是 daspianist 的第一个问题,现在取决于他如何使用修改后的代码。 您应该知道,在完成处理程序中使用运行循环来“等待”异步方法完成是 - 至少 - 徒劳的。由于其他原因,在这里使用运行循环根本不起作用。【参考方案3】:

    如果您没有使用 __block 声明下载的ImageArray,那么它不会从您的 placeHolderImageView setImageWithURLRequest 块中写入;

    考虑使用表格视图而不是滚动视图,以便仅在向用户显示单元格时进行下载,并考虑先下载缩略图,然后再下载整个图像;

【讨论】:

您的第一点不正确。您可以在块中调用非 __block 变量的方法。你不能做的是为变量分配一个新值。

以上是关于如何使用 AFNetworking 从一系列 URL 下载和存储图像?的主要内容,如果未能解决你的问题,请参考以下文章

如何从一系列循环元素中获取点击元素?

如何从一系列照片中计算出恒星旋转?

如何从一系列数组构造熊猫数据框

如何从一系列测量中正确确定 Weibull PDF 参数?

如何从一系列数据属性的元素中更改 css?

如何从一系列文本条目中提取常用/重要短语