0

我已经阅读了大多数与 AFNetworking 的异步性质有关的帖子。但是,我的问题有点独特,我可以使用一些帮助。

我正在从模态视图控制器启动一个新的异步线程,该线程应该告诉用户我正在从 Web 服务器下载内容。这个想法是下载一堆文件(超过 100 个),效果很好。我什至将重试计数放入下载中,以便在失败时重试下载任何文件(最多)。一切都很好下载。问题是我不知道什么时候完成。

原因如下:我正在下载 JSON 文件列表。这些 JSON 文件定义了具有 PDF 和其他类型文件列表的其他 JSON 文件列表。由于我正在下载的内容的性质,我必须按顺序下载它。例如:

  1. 下载文件1.json
  2. 下载后从 file1.json 获取 JSON 文件列表
  3. 加载每个辅助 JSON 文件(subFile1.json、subFile2.json 等)
  4. 下载辅助 JSON 文件 (subFile1.json) 后,我会从该 JSON 文件 (subFile1.json) 中获取要下载的文件列表
  5. 下载 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];
}
4

3 回答 3

0

如果您正在执行 AFHTTPRequestOperation,则可以使用它的 setCompletionBlockWithSuccess 方法设置一个块,一旦下载(甚至上传)完成,就会调用该块。

例子:

AFHTTPRequestOperation *httpreq = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:downloadURL]];
[httpreq setCompetionBlockWithSuccess^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"%@",@"Done!");
}];

此外,您不需要在不同的线程中启动操作,因为它已经异步运行。

于 2013-01-06T05:38:28.540 回答
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链接。

于 2013-01-06T06:36:48.330 回答
0

我会考虑使用 anAFHTTPClient来处理您的下载,特别是因为您想管理多个下载。初始化客户端后,您需要创建所有操作,然后使用方法将它们添加到客户端操作队列中-[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
}];

希望这可以帮助:)

于 2013-07-05T11:09:56.007 回答