使用NSProgress查找下载速度和剩余时间

11
根据 NSProgress文档,我发现-[NSProgress localizedAdditionalDescription]可以报告下载速度和剩余时间,例如:

1.61 GB of 3.22 GB (2 KB/sec) — 2 minutes remaining

然而,当我将NSProgressNSURLSessionDownloadTask关联时,我无法获取这些详细信息。 这是我的代码:

Downloader.h

@interface Downloader : NSObject
@property NSProgress *overallProgress;
-(void)startDownload;
@end

Downloader.m

- (void)startDownload {

    self.overallProgress = [NSProgress progressWithTotalUnitCount:100];

    [self.overallProgress setKind:NSProgressKindFile];
    [self.overallProgress setUserInfoObject:NSProgressFileOperationKindKey forKey:NSProgressFileOperationKindDownloading];


    [self.overallProgress becomeCurrentWithPendingUnitCount:100];
    [self work1];
    [self.overallProgress resignCurrent];
}

- (void)work1 {

    NSProgress *firstTaskProgress = [NSProgress progressWithTotalUnitCount:1];
    [firstTaskProgress setKind:NSProgressKindFile];
    [firstTaskProgress setUserInfoObject:NSProgressFileOperationKindKey forKey:NSProgressFileOperationKindDownloading];

    NSURL *downloadURL = [NSURL URLWithString:@"http://ipv4.download.thinkbroadband.com/200MB.zip"];
    NSURL *destinationDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
    NSURL *destinationURL = [destinationDirectory URLByAppendingPathComponent:[downloadURL lastPathComponent]];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSURLSessionDownloadTask *fileDownloadTask =
    [session downloadTaskWithURL:downloadURL
               completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error){

                   [[NSFileManager defaultManager] removeItemAtURL:destinationURL error:NULL];

                   [[NSFileManager defaultManager] moveItemAtURL:location toURL:destinationURL error:nil];

                   [firstTaskProgress setCompletedUnitCount:1];
               }];

    [fileDownloadTask resume];
}

DownloadObserver.m

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];

    downloader = [Downloader new];

    [downloader addObserver:self
                 forKeyPath:@"overallProgress.fractionCompleted"
                    options:NSKeyValueObservingOptionNew
                    context:NULL];

    [downloader startDownload];

}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"%@", [downloader.overallProgress localizedAdditionalDescription]);
}

这只会打印出:

0 of 100

Zero KB of 100 bytes

我该如何让 localizedAdditionalDescription 打印下载速度和剩余时间?


Marc解决了一个类似的问题。https://dev59.com/H3RC5IYBdhLWcg3wOeWB - sangony
不,我的问题是关于如何让NSProgress为您执行这些计算的。 - Eric
好的...那这个怎么样?https://dev59.com/rnjZa4cB1Zd3GeqPfIl_ - sangony
1个回答

17

首先,totalUnitCount 应该对应文件的大小(以字节为单位)。在下载过程中,我们会更改 completedUnitCount 并反映在 localizedAdditionalDescription 上。 NSProgressKindFile 是格式化这些值为文件大小的提示。

为了正确设置进度,我们需要使用 NSURLSessionDownloadDelegate 方法而不是块处理程序。在 -URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 中,我们拥有所有信息来启动(totalBytesExpectedToWrite)和更新(totalBytesWritten)进度。

static void *myContext = &myContext;

- (void)download {
    NSURL *downloadURL = [NSURL URLWithString:@"http://ipv4.download.thinkbroadband.com/10MB.zip"];
    NSURLSession *session =
    [NSURLSession
     sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
     delegate:self
     delegateQueue:[NSOperationQueue mainQueue]];

    [[session downloadTaskWithURL:downloadURL] resume];
}

#pragma mark - url session download delegate

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    if (!_progress) {
        _progress = [NSProgress progressWithTotalUnitCount:totalBytesExpectedToWrite];
        _progress.kind = NSProgressKindFile;
        [_progress addObserver:self forKeyPath:@"fractionCompleted" options:0 context:myContext];
    }

    _progress.completedUnitCount = totalBytesWritten;
    //[_overallProgress setUserInfoObject:@1024 forKey:NSProgressEstimatedTimeRemainingKey];
}

- (void)URLSession:(NSURLSession *)session 
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    [_progress removeObserver:self forKeyPath:@"fractionCompleted"];
    _progress = nil;
    NSLog(@"finished: %@", location);
}

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
{
    if (context == myContext) {
        self.label.text = [_progress localizedAdditionalDescription];
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

实际上,NSProgress 不进行任何计算。它只根据 userInfo 和其他类属性格式化本地化描述。请参见文档中的 Constants 部分。您可以尝试使用这些值并比较本地化描述输出。

如果您想显示剩余下载时间,可以使用 NSProgressEstimatedTimeRemainingKey。不好的消息是,您需要手动计算剩余时间。参考:如何准确估算剩余下载时间?

我尝试设置 NSProgressThroughputKey,该属性指示每秒处理字节的数据处理速度。我原本期望 NSProgress 会计算剩余时间,但事实并非如此。

另请参阅 AFNetworking 源代码AFURLSessionManagerTaskDelegate 为上传和下载任务使用 NSProgress 实例。


1
谢谢。所以你必须自己进行这些计算!到目前为止,NSProgress只是一个进度数据的封装器...我猜当你将几个NSProgress链接在一起时,它就很有用了..? - Eric
是的,这是一个突出的特点。 - vokilam
@Mojo66:问题得到了彻底的回答。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接