我已经阅读了大部分与AFNetworking的异步性质有关的帖子。但是,我的问题有点独特,我可以使用一些帮助。
我正在从模态视图控制器启动一个新的异步线程,它应该告诉用户我正在从Web服务器下载内容。这个想法是下载一堆文件(超过100个),它的工作效果很好。我甚至将重试计数放入下载中,以便在失败时重新下载任何文件(最多)。一切都下载很棒。问题是我不知道什么时候完成。
这就是原因: 我下载了一个JSON文件列表。这些JSON文件定义了包含PDF和其他类型文件列表的其他JSON文件的列表。由于我正在下载的性质,我必须按顺序下载它。例如:
所以我的进程看起来是分层的,以确保在子文件之前下载父文件: 1.初始下载方法调用SubDownloadMethod2 ON SUCCESS(在成功块内) 2. SubDownloadMethod2从下载的文件中获取列表并调用SubSubDownloadMethod3 3. SubSubDownloadMethod3下载PDF文件(和其他文件)成功(成功块内)
正如您所看到的,我有一个动态数量的文件可供下载。因此,我不知道我将在前面下载多少文件。基于它可以降级到多个级别并来自Web服务器上的多个目录这一事实变得更加困难。
如果下载失败(直到最大重试次数),我也会对每个方法进行递归回调,这也会使其变得更加困难。
因为每次下载都会启动它自己的线程(我假设这就是AFNetworking正在做的事情),我不确定所有的下载是什么时候完成的。我不知道enqueueBatchOfHTTPRequestOperations是否有帮助。我不完全理解它,我从Web服务器上的多个目录下载。我还需要根据每个级别的下载批量操作,因为我不知道在下载和解析定义的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];
}
答案 0 :(得分:0)
如果你正在进行AFHTTPRequestOperation,你可以使用它的setCompletionBlockWithSuccess方法来设置一个块,一旦下载(甚至上传)完成就会被调用。
示例:
AFHTTPRequestOperation *httpreq = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:downloadURL]];
[httpreq setCompetionBlockWithSuccess^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"%@",@"Done!");
}];
此外,您不需要在异步运行的其他线程中启动操作。
答案 1 :(得分:0)
你在NSOperationQueue中添加所有操作,如下所示:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation: operation]; //your operation here
[operation start]
[operation waitUntilFinish]
如何维护NSOperationQueue,请参阅以下链接:
参考ios-how-to-know-when-nsoperationqueue-finish-processing-a-few-operations链接。
参考ios-how-to-check-if-an-nsoperation-is-in-an-nsoperationqueue链接。
参考ios-nsoperationqueue-operations-all-run-when-added-and-dont-queue链接。
答案 2 :(得分:0)
我会考虑使用AFHTTPClient
来处理您的下载,尤其是因为您想要管理多个下载。初始化客户端后,您需要创建所有操作,然后使用方法-[AFHTTPClient enqueueBatchOfHTTPRequestOperations: progressBlock: completionBlock:]
将它们添加到客户端操作队列中。这样,您还可以使用进度块确定下载进度。此外,完成块将允许您在完成时执行自己的自定义代码。
所以你要从初始化你的客户开始:
AFHTTPClient *client =[AFHTTPClient clientWithBaseURL:baseURL];
然后,您要创建HTTP请求操作:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
AFHTTPRequestOperation *firstOperation = [client HTTPRequestOperationWithRequest:request success:nil failure:nil];
AFHTTPRequestOperation *secondOperation = [client HTTPRequestOperationWithRequest:request success:nil failure:nil];
然后,您只需将这些操作添加到客户端操作队列:
[client enqueueBatchOfHTTPRequestOperations:@[firstOperation, secondOperation, thirdImageRequestOperation]
progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
//Handle progress here
}
completionBlock:^(NSArray *operations) {
//Handle completion of all downloads here
}];
希望这会有所帮助:)