2

我想将大量(800-2000)图像从服务器保存到 iPhone 应用程序目录(文档目录)。

首先,我必须检查这些图像是否已经在 iPhone 目录中。

我使用此代码下载一张图片:

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[NSURLConnection connectionWithRequest:request delegate:self];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *localFilePath = [documentsDirectory stringByAppendingPathComponent:@"pkm.jpg"];
NSData *thedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://a3.twimg.com/profile_images/414797877/05052008321_bigger.jpg"]];
[thedata writeToFile:localFilePath atomically:YES];

请帮助我,任何建议: 1. 使用 iPhone 目录中的原始名称保存所有图像,而不是“pkm.jpg” 2. 使用名称保存图像:从 05052008321_bigger 到 05052008350_bigger 3. 检查图像是否已经下载,不要下载再次。

我知道,也许这不仅仅是一个问题。但是一些建议,方向会非常好。

提前致谢。

4

2 回答 2

2

几个反应:

  1. 您正在启动 a NSURLConnection connectionWithRequest(触发NSURLConnectionDataDelegate方法),但您显然忽略了这一点并启动了dataWithContentsOfURL. 你应该选择一个或另一个,但不要两者都做。

  2. 我建议您采用NSOperation基于 - 的解决方案,因为您肯定希望 (a) 享受并发;但是(b)将并发操作限制在某个合理的数量(比如 4 或 5),否则您将有请求超时并失败。

  3. 在获取文件名方面,您可以使用lastPathComponentNSURL. (我的下载操作,在下面使用,如果你不提供明确的文件名,实际上会自动使用它。)

  4. 您还没有说明如何确定来自远程服务器的文件名列表,因此我们必须看看您如何知道要检索哪些图像。

  5. 如果这是出于您自己的目的,那很好,但我听说苹果拒绝通过蜂窝连接请求过多数据的应用程序(并且 2000 张图像肯定符合条件)。坦率地说,即使 Apple 没有大惊小怪,你也应该在使用他们的大量数据计划之前询问用户。您可以使用Reachability来确定用户是通过 wifi 还是通过蜂窝网络连接,如果是后者,您可能需要发出警告。

但我建议一些看起来像的东西(假设你有一些NSArray版本NSString的 URL ......显然调整这个为你的 URL 列表的任何形式):

NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

for (NSString *urlString in urlStrings)
{
    NSURL *url = [NSURL URLWithString:urlString];
    NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
    if (![fileManager fileExistsAtPath:path]) {
        DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url];
        downloadOperation.downloadCompletionBlock = ^(DownloadOperation *operation, BOOL success, NSError *error) {
            if (error) NSLog(@"download of %@ failed: %@", operation.url, error);
        };
        [downloadQueue addOperation:downloadOperation];
    }
}

而那个下载操作可能是这样的。显然,使用NSOperation您想要的任何基于下载器,但我建议您使用一种不会将整个下载加载到内存中的下载器,而是直接将其流式传输到持久存储的下载器。

如果您不想花哨,则可以执行以下操作:

NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount = 4;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

for (NSString *urlString in urlStrings)
{
    NSURL *url = [NSURL URLWithString:urlString];
    NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
    if (![fileManager fileExistsAtPath:path]) {
        [downloadQueue addOperationWithBlock:^{
            NSString *path = [docsPath stringByAppendingPathComponent:[url lastPathComponent]];
            NSData *data = [NSData dataWithContentsOfURL:url];
            if (data)
                [data writeToFile:path atomically:YES];
        }];
    }
}

显然,使用您想要下载的任何下载操作类,但希望您了解基本概念。创建一个NSOperation-based 下载,然后为每个需要下载的文件提交一个。

于 2013-08-12T02:46:14.123 回答
1

我不确定序列化信息的最佳方法(天真地,您可以将 NSDictionary 写入磁盘)。我会有一个大的 NSDictionary (它可以分解成更小的,取决于你如何做到这一点)。NSDictionary 将采用图像名称“05052008321_bigger”并将其映射到简单的@YES。

当应用程序可以下载新图像时,它会从磁盘读取 NSDictionary(可以在不同的线程中读取),并检查图像名称是否在字典中。这允许快速查找。

- (BOOL)checkIfImageHasBeenDownloaded:(NSString *)imageName
{
    return [self.dictionaryNames objectForKey:imageName] != nil;
}
于 2013-08-12T02:33:36.157 回答