我已经阅读了大多数与 AFNetworking 的异步性质有关的帖子。但是,我的问题有点独特,我可以使用一些帮助。
我正在从模态视图控制器启动一个新的异步线程,该线程应该告诉用户我正在从 Web 服务器下载内容。这个想法是下载一堆文件(超过 100 个),效果很好。我什至将重试计数放入下载中,以便在失败时重试下载任何文件(最多)。一切都很好下载。问题是我不知道什么时候完成。
原因如下:我正在下载 JSON 文件列表。这些 JSON 文件定义了具有 PDF 和其他类型文件列表的其他 JSON 文件列表。由于我正在下载的内容的性质,我必须按顺序下载它。例如:
- 下载文件1.json
- 下载后从 file1.json 获取 JSON 文件列表
- 加载每个辅助 JSON 文件(subFile1.json、subFile2.json 等)
- 下载辅助 JSON 文件 (subFile1.json) 后,我会从该 JSON 文件 (subFile1.json) 中获取要下载的文件列表
- 下载 JSON 子文件中指定的每个文件(例如 subFile1.json)
所以我的过程看起来是分层的,以确保在子文件之前下载父文件: 1. 初始下载方法在成功时调用 SubDownloadMethod2(在成功块内) 2. SubDownloadMethod2 从下载的文件中获取列表并调用 SubSubDownloadMethod3 3. SubSubDownloadMethod3 下载 PDF 文件(和其他文件) ) ON SUCCESS (在成功块内)
如您所见,我有动态数量的文件要下载。因此我不知道我会预先下载多少文件。由于它可以进入多个级别并来自 Web 服务器上的多个目录,因此它变得更加困难。
如果下载失败(达到最大重试次数),我也会对每个方法进行递归回调,这也使它变得更加困难。
因为每次下载都会启动它自己的线程(我假设这就是 AFNetworking 正在做的事情),所以我不确定所有下载何时完成。我不知道 enqueueBatchOfHTTPRequestOperations 是否有帮助。我不完全理解它,我正在从网络服务器上的多个目录下载。我还需要根据每个下载级别进行批处理操作,因为我不知道在下载和解析定义的 JSON 文件之前我会走多远。
帮助!!!!
我认为将代码放入其中可能会有所帮助。要查看的代码很多,但这是一个难题(嗯,对于具有我技能的人来说)。
如果你看代码,每次我需要下载另一个文件时,我都会调用最底部的方法:
- (void)downloadLibraryFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount completionBlock:(DownloadLibraryFileCompletionBlock)completionBlock
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:fileOnServer]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:targetFile append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(@"Successfully downloaded file to %@", path);
completionBlock(fileOnServer, targetFile, retryCount, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionBlock(fileOnServer, targetFile, retryCount, error);
}];
[operation start];
}
这将启动 AFNetorking 并开始下载。完成后它会调用我的完成块,但我怎么知道它们什么时候完成?
这是剩下的代码(包括上面的方法)
- (void)downloadLibraryOnReset
{
// Find and copy the page defintion file to the documents directoy
// TODO localize the call to get the appropriate file based on language
dispatch_queue_t queue = dispatch_queue_create("Library Download Queue", NULL);
dispatch_async(queue, ^{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
// Save server root URL
self.serverRootURL = serverLibraryURL;
// Add last component onto download path
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitions];
// Get target location
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);;
NSString *targetFile = [dirPaths objectAtIndex:0];
targetFile = [targetFile stringByAppendingPathComponent:@"en"]; // TODO this needs to be localized based on language
targetFile = [targetFile stringByAppendingPathComponent:kPageDefinitionsDirectory];
self.pageDefintiionDirectoryURL = targetFile;
// Create the subdirectory off of the documents directory to contain the config files
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr createDirectoryAtPath:targetFile withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
NSString *pageDefinitionsFileURL = [targetFile stringByAppendingPathComponent:kPageDefinitions];
[self downloadPageDefinitionsFile:serverLibraryURL targetFile:pageDefinitionsFileURL retryCount:kDownloadRetryCount];
// Reset the resetContent flag to false
[defaults setBool:NO forKey:kResetContent];
});
}
- (void)downloadPageDefinitionsFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount
{
[self downloadLibraryFile:fileOnServer targetFile:targetFile retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
[self downloadPageDefinitionsFile:fileOnServer targetFile:targetFile retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), fileOnServer);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
}
// Copy down all config files defined in the pagedefinitions.json file
//code from other library
NSError* err = nil;
NSString *path = self.pageDefintiionDirectoryURL;
path = [path stringByAppendingPathComponent:kPageDefinitions];
NSData *data = [NSData dataWithContentsOfFile:path];
if(data)
{
// Convert to JSON Directory
NSMutableDictionary *pageDefinitionsDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
//fileVersion = pageDefinitionsDict[kFileVersion];
NSMutableArray *pages = pageDefinitionsDict[kPages];
for (NSMutableDictionary *page in pages)
{
MINPageDefinition *pageDef = [[MINPageDefinition alloc] initWithDictionary:page];
NSString *targetDirectory = [targetFile stringByDeletingLastPathComponent];
pageDef.pageURL = [targetDirectory stringByAppendingPathComponent:pageDef.pageConfigFileName];
//NSString *imageURL = [pageDef.pageURL stringByDeletingLastPathComponent];
pageDef.pageImageURL = [self.pageDefintiionDirectoryURL stringByAppendingPathComponent:pageDef.pageImageName];
[[MINPageStore sharedInstance].pageArray addObject:pageDef];
}
// Write modified pagedefinitions.json to the appropriate directory in Documents
[[MINPageStore sharedInstance] writePageDefinitionFile:path];
// Continue downloading page images and other config files,
for (MINPageDefinition *currPage in [[MINPageStore sharedInstance] pageArray])
{
[self downloadPageDefinitionImageFile:currPage retryCount:kDownloadRetryCount];
[self downloadPageDefinitionJSONFile:currPage retryCount:kDownloadRetryCount];
}
}
}
}];
}
- (void)downloadPageDefinitionJSONFile:(MINPageDefinition *)pageDef retryCount:(int)retryCount
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:pageDef.pageConfigFileName];
[self downloadLibraryFile:serverLibraryURL targetFile:pageDef.pageURL retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
[self downloadPageDefinitionJSONFile:pageDef retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
// Copy down all config files defined in the pagedefinitions.json file
if([pageDef.pageType isEqualToString:kGridView])
{
[self downloadGridViewContent:pageDef];
}
else
{
//NSLog(@">>>>FINISHED DOWNLOADING PAGE: %@", pageDef.pageName);
}
}
}];
}
- (void)downloadPageDefinitionImageFile:(MINPageDefinition *)pageDef retryCount:(int)retryCount
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *serverLibraryURL = [defaults objectForKey:kRootURL];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:kPageDefinitionsDirectory];
serverLibraryURL = [serverLibraryURL stringByAppendingPathComponent:pageDef.pageImageName];
[self downloadLibraryFile:serverLibraryURL targetFile:pageDef.pageImageURL retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
[self downloadPageDefinitionImageFile:pageDef retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), serverLibraryURL);
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.masterViewController.tableView reloadData];
});
}
}];
}
- (void)downloadGridViewContent:(MINPageDefinition *)pageDef
{
// Parse off the json extension
// Use this to create a subdirectory under the pagedefinitions directoy
NSString *newDirectoryForGridView = [pageDef.pageURL stringByDeletingPathExtension];
newDirectoryForGridView = [newDirectoryForGridView lastPathComponent];
NSString *newGridViewDirectoryURL = [pageDef.pageURL stringByDeletingPathExtension];
// Create the subdirectory off of the documents directory to contain the config files
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr createDirectoryAtPath:newGridViewDirectoryURL withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
// Load the grid view config file
[MINVolume loadAlbumItems:pageDef.pageURL completionBlock:^(NSString *fileName, MINVolume *newVolume, NSError *error) {
if(!error)
{
if(newVolume && [newVolume.albumsArray count] > 0)
{
// Iterate through the albums and create directories for each album
for(MINAlbum *album in newVolume.albumsArray)
{
NSString *localAlbumDirectory = [newGridViewDirectoryURL stringByAppendingPathComponent:album.albumURL];
if ([filemgr createDirectoryAtPath:localAlbumDirectory withIntermediateDirectories:YES attributes:nil error: NULL] == NO)
{
// Failed to create directory
}
// Copy down all album content
for(MINAlbumItem *albumItem in album.albumItemsArray)
{
// Create names for local file and thumbnail
NSString *localAlbumItemFileURL = [localAlbumDirectory stringByAppendingPathComponent:albumItem.itemFileName];
NSString *localAlbumItemFileThumbURL = [localAlbumDirectory stringByAppendingPathComponent:albumItem.itemThumbnailImageName];
// Define paths for file and thumbnail on server
NSString *serverAlbumItemFileURL = [self.serverRootURL stringByAppendingPathComponent:newDirectoryForGridView];
serverAlbumItemFileURL = [serverAlbumItemFileURL stringByAppendingPathComponent:album.albumURL];
serverAlbumItemFileURL = [serverAlbumItemFileURL stringByAppendingPathComponent:albumItem.itemFileName];
NSString *serverAlbumItemFileThumbURL = [self.serverRootURL stringByAppendingPathComponent:newDirectoryForGridView];
serverAlbumItemFileThumbURL = [serverAlbumItemFileThumbURL stringByAppendingPathComponent:album.albumURL];
serverAlbumItemFileThumbURL = [serverAlbumItemFileThumbURL stringByAppendingPathComponent:albumItem.itemThumbnailImageName];
// Copy album item file
BOOL bFileExists = [filemgr fileExistsAtPath:localAlbumItemFileURL];
if(!bFileExists)
{
[self downloadAlbumItem:albumItem isThumbnail:(BOOL)false fileOnServer:serverAlbumItemFileURL targetFile:localAlbumItemFileURL retryCount:kDownloadRetryCount];
}
else
{
albumItem.itemURL = localAlbumItemFileURL;
}
// Copy album item thumbnail
BOOL bFileThumbnailExists = [filemgr fileExistsAtPath:localAlbumItemFileThumbURL];
if(!bFileThumbnailExists)
{
[self downloadAlbumItem:albumItem isThumbnail:true fileOnServer:serverAlbumItemFileThumbURL targetFile:localAlbumItemFileThumbURL retryCount:kDownloadRetryCount];
}
else
{
albumItem.itemThumbnailURL = localAlbumItemFileThumbURL;
}
}
}
}
else
{
NSLog(@"No volume found for file: %@", pageDef.pageConfigFileName);
}
}
}];
}
- (void)downloadAlbumItem:(MINAlbumItem *)albumItem isThumbnail:(BOOL)isThumbnail fileOnServer:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount
{
[self downloadLibraryFile:fileOnServer targetFile:targetFile retryCount:retryCount completionBlock:^(NSString *fileOnServer, NSString *targetFile, int retryCount, NSError *error) {
if(error)
{
retryCount--;
if(retryCount)
{
NSLog(@"RETRY DONWLOAD (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
[self downloadAlbumItem:albumItem isThumbnail:isThumbnail fileOnServer:fileOnServer targetFile:targetFile retryCount:retryCount];
}
else
{
NSLog(@"RETRY COUNT EXCEEDED (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount, NSStringFromSelector(_cmd), fileOnServer);
}
}
else
{
// Check to see if this file was downloaded after an error
if(retryCount < kDownloadRetryCount)
{
NSLog(@">>RETRY SUCCESSFUL (Attempt: %d, Method: %@) > File: %@", kDownloadRetryCount - retryCount, NSStringFromSelector(_cmd), fileOnServer);
}
if(isThumbnail)
albumItem.itemThumbnailURL = targetFile;
else
albumItem.itemURL = targetFile;
}
}];
}
- (void)downloadLibraryFile:(NSString *)fileOnServer targetFile:(NSString *)targetFile retryCount:(int)retryCount completionBlock:(DownloadLibraryFileCompletionBlock)completionBlock
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:fileOnServer]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:targetFile append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//NSLog(@"Successfully downloaded file to %@", path);
completionBlock(fileOnServer, targetFile, retryCount, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionBlock(fileOnServer, targetFile, retryCount, error);
}];
[operation start];
}