2013-01-06 32 views
0

我已經經歷最具有與AFNetworking的異步性質做崗位的讀取文件。但是,我的問題有點獨特,我可以使用一些幫助。如何確定,當我完成下載與AFNetworking

我從模態視圖控制器開始一個新的異步線程,應該告訴用戶我正在從Web服務器上下載內容。這個想法是下載一堆文件(超過100),它的工作很好。我甚至會將重試計數放入下載中,以便在下載任何文件失敗時重試(最大)。一切都在下載很好。問題是我不知道什麼時候完成。

這是爲什麼: 我下載了一個JSON文件列表。這些JSON文件定義了其他具有PDF和其他類型文件列表的JSON文件的列表。由於我正在下載的內容,我必須按順序下載。例如:

  1. 下載file1.json
  2. 它的下載
  3. 負荷後從file1.json JSON文件
  4. 獲取列表這些二次JSON文件(subFile1.json,subFile2.json等)
  5. 二次JSON文件下載(subFile1.json)後,我得到的文件列表從該JSON文件下載(subFile1.json)將在JSON子文件中指定的文件
  6. 下載(subFile1.json爲例如)

所以我的過程看起來層次,以保證母文件子文件之前獲取下載: 1.初始下載方法調用SubDownloadMethod2成功(成功區塊內) 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

如果你正在做一個AFHTTPRequestOperation,你可以用它來設置一個塊setCompletionBlockWithSuccess方法,將被調用一次下載(甚至上傳)完成。

例子:

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

而且,你不需要在不同的線程的動作開始,因爲它已經異步運行。

+0

我發表了上面的代碼。正如你所看到的,我正在使用完成塊。問題是完成塊只告訴我什麼時候下載了一個特定的文件,而不是當我已經下載了所有的文件下載時。我希望這是有道理的。我完全沒有關於如何完成這項工作的想法。 – JustLearningAgain

+1

我沒有足夠的代表直接評論@王子的答案,所以我會把它放在這裏。 [操作waitUntilFinish]不好。它會掛起你運行它的線程,如果它是你的應用程序看起來掛起的主線程。 – coolstar

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鏈接。

+0

我認爲這是正確的道路。我想如果我把下載順序分成幾部分。 1.入隊下載JSON文件2.排隊下載JSON文件中列出的文件,這將起作用。我有2個問題。 1.如何在隊列啓動後將隊列重新添加到入隊隊列中。如果操作失敗,我想重新添加它以重試下載。 2.我可以創建入隊隊列嗎?我想知道是否可以在兩個排隊操作隊列之間設置依賴關係,以便第二個隊列在第一個隊列完成之前不會啓動? – JustLearningAgain

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 
}]; 

希望這會有所幫助:)