2013-12-20 22 views
18

NSURLSession將允許您在其後添加大量的NSURLSessionTask以供下載。NSURLSession中所有NSURLSessionTasks的平均進度

如果要檢查單個NSURLSessionTask的進步,它是那麼容易,因爲

double taskProgress = (double)task.countOfBytesReceived/(double)task.countOfBytesExpectedToReceive;

但是,什麼是檢查的所有NSURLSessionTasksNSURLSession平均進展的最佳方式是什麼?

我想我會嘗試平均的所有任務的進度:

[[self backgroundSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *allDownloadTasks) { 

    double totalProgress = 0.0; 

    for (NSURLSessionDownloadTask *task in allDownloadTasks) { 

     double taskProgress = (double)task.countOfBytesReceived/(double)task.countOfBytesExpectedToReceive; 

     if (task.countOfBytesExpectedToReceive > 0) { 
      totalProgress = totalProgress + taskProgress; 
     } 
     NSLog(@"task %d: %.0f/%.0f - %.2f%%", task.taskIdentifier, (double)task.countOfBytesReceived, (double)task.countOfBytesExpectedToReceive, taskProgress*100); 
    } 

    double averageProgress = totalProgress/(double)allDownloadTasks.count; 

    NSLog(@"total progress: %.2f, average progress: %f", totalProgress, averageProgress); 
    NSLog(@" "); 

}]; 

但這裏的邏輯是錯的:假設你有50個任務,希望下載1MB和3個任務預計下載100MB。如果50個小任務在3個大任務之前完成,則averageProgress將遠遠高於實際的平均進度。

所以,你必須根據TOTALcountOfBytesReceivedTOTALcountOfBytesExpectedToReceive分計算平均進度。但問題是NSURLSessionTask只有在啓動時才計算出這些值,並且在另一個任務完成之前它可能不會啓動。

那麼如何檢查所有NSURLSessionTasksNSURLSession的平均進度?

+1

您可以爲每個文件的每個文件發送一個Header請求,爲您的整體進度添加「Content-Length」。 – HAS

+0

這將工作。但就我而言,我可能會碰到一次必須啓動200次下載的情況,因此所有相應的頭文件請求都需要很長時間。儘管如此,這種方法值得詳細闡述,所以如果你把它作爲答案發布,那將是值得的。 – Eric

+0

由於您正在處理200多個項目,爲什麼不讓進度成爲在總數中完成的文件數?我們這樣做是爲了下載超過300個圖像,它絕對矯枉過正,試圖計算確切的字節比例?我的兩分錢:) –

回答

13

在開始下載文件之前,您可以發送很小的文件HEAD requests來獲取文件大小。您只需添加他們的expectedContentLength並擁有您的最終下載大小。

- (void)sizeTaskForURL:(NSURL *)url 
{ 

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 
    [request setHTTPMethod:@"HEAD"]; 

    NSURLSessionDataTask *sizeTask = 
    [[[self class] dataSession] 
    dataTaskWithRequest:request 
    completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) 
    { 
     if (error == nil) 
     { 
      NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 

      if ([httpResponse statusCode] == 200) { 

       totalBytesExpectedToReceive += (double)[httpResponse expectedContentLength]; 

       numberOfFileSizesReceived++; 

       NSLog(@"%lu/%lu files found. file size: %f", (unsigned long)numberOfFileSizesReceived, (unsigned long)numberOfTasks, totalBytesExpectedToReceive); 

       if (numberOfFileSizesReceived == numberOfTasks){ 
        NSLog(@"%lu/%lu files found. total file size: %f", (unsigned long)numberOfFileSizesReceived, (unsigned long)numberOfTasks, totalBytesExpectedToReceive); 
       } 
      } 
      else { 
       NSLog(@"Bad status code (%ld) for size task at URL: %@", (long)[httpResponse statusCode], [[response URL] absoluteString]); 
      } 
     } 
     else 
     { 
      NSLog(@"Size task finished with error: %@", error.localizedDescription); 
     } 
    }]; 

    [sizeTask resume]; 

} 

下載文件之後

這是它的樣子:

在左邊你可以看到,當它發出的HEAD requests它還沒有啓動UIProgressView。完成後下載文件。

App

所以,如果你下載大文件可能是「浪費」那些秒使HEAD請求,並從此向用戶顯示正確的進步,而不是一些錯誤的進度。

在下載過程中,您(絕對)想要使用委託方法來獲取較小的新數據細分(否則進度視圖將「跳轉」)。

15

啊是的。我記得在1994年,當我寫OmniWeb的時候,我回憶起了這件事。我們嘗試了許多解決方案,包括僅僅讓進度條旋轉而不是顯示進度(不受歡迎),或者隨着新任務計算出他們將被添加到隊列中的程度會增加(讓用戶感到不安,因爲他們看到了有時會逆轉進展)。最後,大多數程序決定使用(包括iOS 5和Safari中的消息)是一種騙局:例如,在消息中,他們知道發送消息的平均時間約爲1.5秒(例如,只有數字),所以他們動畫進度條大約在1.5秒完成,並且如果消息還沒有實際發送,它只會延遲到1.4秒。

現代瀏覽器(如Safari)通過將任務分成多個部分並顯示每個部分的進度條來改變此方法。就像(僅用於示例),Safari可能會認爲在DNS中查找URL通常需要0.2秒,因此它們會在0.2秒內爲進度條的前十分之一(或其他)製作動畫,但他們當然會如果DNS查找分別需要更短或更長時間,請跳過(或等待1/10分)。

在你的情況下,我不知道你的任務有多可預測,但應該有一個類似的作弊。比如,大多數文件的平均大小是多少?如果是這樣,你應該能夠弄清楚50將會花多長時間。或者,您只需將進度條劃分爲50個段,並在每次文件完成時填寫段,然後根據當前獲得的字節數/秒或根據您獲得的文件數/秒數進行動畫製作遠或其他任何你喜歡的指標。

一個訣竅是如果你必須啓動或停止進度條,不要停下來或跳到下一個標記,而應該減慢(並保持減速)或加速(並保持加快速度),直到你已經到達了酒吧所需的位置。

祝你好運!

+1

只有在您*準確*提前知道您正在下載的每個**文件的平均大小時,它才能正常工作。但是,如果你沒有提前知道這些知識(包括我的許多人的情況),這種方法只是複雜的猜測。做芝諾的伎倆是一個陰謀。 – Eric

+0

無論如何,我會放大你的答案,因爲我發現你的解釋清楚,有趣的歷史:-) – Eric

+0

它經常運作,因爲除非文件很大,很多時間只是建立連接/開始取/等的開銷。所以從1-1000字節的任何文件基本上是同一時間獲取。而且,就像我說的那樣,這是最先進的。 –

2

我有兩種可能的解決方案給你。既不準確地找到你正在尋找的東西,也能讓用戶瞭解正在發生的事情。

您有多個進度條的第一個解決方案。指示文件編號進度的一個大條(200箇中有50個)。然後你在它下面有多個進度條(它們的數量等於可能下載的併發量,在我的情況下這是4,在你的使用中可能很難處理)。因此,用戶知道關於下載的細節細節,並獲得整體總體進度(即不隨下載字節移動,但下載完成)。

第二個解決方案是一個多文件進度條。這可能是欺騙,因爲文件大小不同,但是您可以創建一個進度條,並將其分割成與您的文件下載計數相同的許多塊。然後,基於單個文件的下載,每個塊的塊從0%到100%。因此,您可以在開始和結束爲空(文件未下載)時填入進度欄的中間部分。

同樣,這些解決方案都不能解決如何從多個文件中下載總字節數的問題,但它們是可選的用戶界面,因此用戶能夠理解在任何給定時間發生的一切。 (我最喜歡選項1)

3

您的問題是「進度條」問題的一個特例。舉例來說,這reddit thread

它已經繞永遠,並作爲Wil似乎在說,它可以bedevilingly很難甚至Megacorp公司™獲得「只是如此」。例如。即使是完全準確的MB值,只是由於網絡問題,您可以輕鬆掛起而無需「進度」。你的應用程序仍在工作,但的感覺可能是你的程序被掛起。

追求提供極其「準確」的價值觀可能使得過程複雜化。仔細踩踏。