Ios NSURLSession中所有NSURLSessionTask的平均进度

Ios NSURLSession中所有NSURLSessionTask的平均进度,ios,macos,cocoa-touch,cocoa,nsurlsession,Ios,Macos,Cocoa Touch,Cocoa,Nsurlsession,NSURLSession将允许您添加大量的NSURLSessionTask在后台下载 如果要检查单个NSURLSessionTask的进度,只需 double task progress=(double)task.countofbytes接收/(double)task.countofbytes预期接收 但是,检查NSURLSessionTasks中所有NSURLSessionTasks的平均进度的最佳方法是什么 我想我应该尝试平均所有任务的进度: [[self backgroundSession

NSURLSession
将允许您添加大量的
NSURLSessionTask
在后台下载

如果要检查单个
NSURLSessionTask
的进度,只需

double task progress=(double)task.countofbytes接收/(double)task.countofbytes预期接收

但是,检查
NSURLSessionTasks
中所有
NSURLSessionTasks
的平均进度的最佳方法是什么

我想我应该尝试平均所有任务的进度:

[[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
将远高于实际平均进度

因此,您必须根据接收到的字节数除以接收到的字节数来计算平均进度。但问题是
NSURLSessionTask
只在启动时计算出这些值,并且可能在另一个任务完成后才会启动


那么,如何检查
NSURLSession中所有
nsurlsessiontask
的平均进度

啊,是的。我记得我在1994年写OmniWeb时处理过这个问题。我们尝试了很多解决方案,包括只是让进度条旋转而不是显示进度(不受欢迎),或者让它随着新任务的增加而增长(让用户感到不安,因为他们有时会看到反向进度)

最后,大多数程序决定使用的(包括iOS 5和Safari中的消息)是一种欺骗:例如,在消息中,他们知道发送消息的平均时间约为1.5秒(仅示例数字),因此他们将进度条设置为1.5秒左右,如果消息还没有被发送出去,它只会延迟1.4秒

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

在你的情况下,我不知道你的任务有多可预测,但应该有一个类似的欺骗。比如,大多数文件都有平均大小吗?如果是这样的话,你应该能够计算出50分钟需要多长时间。或者,您只需将进度条分成50个部分,并在每次文件完成时填充一个部分,然后根据当前获得的字节数/秒或当前获得的文件数/秒或任何其他指标设置动画

一个技巧是使用Zeno的悖论,如果你必须启动或停止进度条,不要只是停止或跳到下一个标记,而是放慢(并保持放慢)或加快(并保持加快),直到你到达进度条需要的位置


祝你好运

我有两种可能的解决方案。两者都不能得到你想要的东西,但都能让用户了解正在发生的事情

第一个解决方案是有多个进度条。一个大条,指示文件编号进度(完成200个中的50个)。然后在下面有多个进度条(它们的数量等于可能的并发下载量,在我的例子中是4,在你的例子中,这可能是不方便的)。因此,用户了解下载的详细信息,并获得总体进度(不随下载字节移动,而是随下载完成而移动)

第二种解决方案是多文件进度条。这可能是欺骗,因为文件大小不同,但您可以创建一个进度条,将其切割成与文件下载数量相等的块。然后,基于单个文件的下载,条的每个块从0%到100%。因此,您可以填充进度条的中间部分,而开始和结束部分为空(未下载文件)


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

在开始下载文件之前,您可以发送微小的
头部请求来获取文件大小。您只需添加它们,就可以获得最终的下载大小

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

}
之后下载文件

这就是它的样子:

在左侧,您可以看到,当它发送
头部请求时,它尚未启动
UIProgressView
。完成后,它将下载文件

因此,如果你下载了大文件,那么“浪费”这些秒来提出HEAD请求,然后向用户显示正确的进度,而不是错误的进度,可能会很有用


在下载过程中,您(肯定)希望使用委托方法获得新数据的较小细分(否则进度视图将“跳转”)。

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

它已经存在了很久,正如人们所说的那样,即使是对于Megacorp,Inc.来说,它也可能非常困难。™ 得到“只是这样”。例如,即使使用完全准确的MB值,您也可以轻松挂起,而不会出现“进度”,这仅仅是因为网络问题。你的应用程序仍在运行,但感觉可能是你的程序挂起了

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