18

AnNSURLSession将允许您在后台添加大量NSURLSessionTask下载。

如果你想查看一个单曲的进度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将远远高于实际平均进度。

所以你必须根据TOTAL countOfBytesReceived除以TOTAL countOfBytesExpectedToReceive来计算平均进度。但问题是 a仅在启动时才NSURLSessionTask计算出这些值,并且可能要等到另一个任务完成后才会启动。

那么如何检查a中所有的平均进度NSURLSessionTasksNSURLSession呢?

4

4 回答 4

15

是啊。我记得在 1994 年我写 OmniWeb 时处理过这个问题。我们尝试了许多解决方案,包括只是让进度条旋转而不是显示进度(不流行),或者让它随着新任务计算出它们将有多大/被添加到队列中而增长(让用户感到不安,因为他们看到有时会逆转进展)。

最后,大多数程序决定使用(包括 iOS 5 中的 Messages 和 Safari)是一种欺骗:例如,在 Messages 中,他们知道发送消息的平均时间约为 1.5 秒(仅示例数字),所以他们让进度条在大约 1.5 秒后完成,如果消息还没有真正发送,他们只会延迟 1.4 秒。

现代浏览器(如 Safari)通过将任务划分为多个部分并为每个部分显示一个进度条来改变这种方法。就像(仅示例)一样,Safari 可能会认为在 DNS 中查找 URL 通常需要 0.2 秒,因此他们会在 0.2 秒内为进度条的前 1/10(或其他)设置动画,但他们当然会如果 DNS 查找分别需要更短或更长的时间,请向前跳过(或等待 1/10 标记)。

在你的情况下,我不知道你的任务有多可预测,但应该有类似的作弊。比如,大多数文件有平均大小吗?如果是这样,您应该能够计算出 50 需要多长时间。或者,您只需将进度条分成 50 个段,并在每次文件完成时填写一个段,并根据您获得的当前字节/秒数或基于您获得的文件数/秒进行动画处理far 或您喜欢的任何其他指标。

如果你必须开始或停止进度条,一个技巧是使用芝诺悖论——不要只是停止或跳到下一个标记,而是要放慢(并继续放慢)或加速(并继续加速)直到你已经到了酒吧需要的地方。

祝你好运!

于 2014-01-14T13:46:48.930 回答
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. 完成后,它会下载文件。

应用程序

因此,如果您下载大文件,“浪费”发出 HEAD 请求的那几秒钟可能会很有用,从而向用户显示正确的进度,而不是一些错误的进度。

在下载期间,您(肯定)希望使用委托方法来获得新数据的更小细分(否则进度视图将“跳跃”)。

于 2014-01-14T20:43:41.460 回答
3

您的情况是“进度条”问题的一个具体案例。授予,例如这个reddit 线程

它一直存在正如Wil似乎在说的那样,即使是 Megacorp, Inc.™ 也很难做到“如此”。例如,即使使用完全准确的 MB 值,您也可以轻松挂起而没有“进展”,只是由于网络问题。您的应用程序仍在运行,但您可能会认为您的程序已挂起。

寻求提供极其“准确”的值可能会使正在进行的任务过于复杂。小心行事。

于 2014-01-20T19:17:42.033 回答
2

我有两个可能的解决方案给你。两者都不能准确地得到您正在寻找的东西,但都可以让用户了解正在发生的事情。

第一个解决方案,您有多个进度条。一个指示文件编号进度的大条(已完成 200 个中的 50 个)。然后您在其下方有多个进度条(它们的数量等于可能的并发下载量,在我的情况下是 4,在您的情况下它可能很笨拙)。因此,用户既了解下载的细粒度细节,又了解总体总进度(不随下载字节移动,但随着下载完成而移动)。

第二种解决方案是多文件进度条。这可能具有欺骗性,因为文件大小不同,但您可以创建一个进度条,将其切成与文件下载计数相等的块数。然后根据单个文件的下载,条形的每个块从 0% 变为 100%。因此,当开头和结尾为空(未下载文件)时,您可以填充进度条的中间部分。

同样,这些都不是解决如何从多个文件中下载总字节数的问题的解决方案,但它们是替代 UI,因此用户可以了解在任何给定时间发生的一切。(我最喜欢选项1)

于 2014-01-14T17:54:41.860 回答