【问题标题】:How to get download progress in AFNetworking 2.0?如何在 AFNetworking 2.0 中获取下载进度?
【发布时间】:2013-10-09 07:39:42
【问题描述】:

我正在使用 AFURLSessionManager 创建一个新的下载任务:

AFURLSessionManager* manager = ...

NSProgress* p = nil;
NSURLSessionDownloadTask* downloadTask =
        [manager downloadTaskWithRequest:request
                                 progress:&p
                              destination:^NSURL*(NSURL* targetPath, NSURLResponse* response) {...}
                        completionHandler:^(NSURLResponse* response, NSURL* filePath, NSError* error) {...}
        ];
[downloadTask resume];

文件可以正常下载,但是如何获得进度通知?

p 始终设置为零。我已经为此提交了issue

我也尝试在管理器上调用setDownloadTaskDidWriteDataBlock,我确实在那里收到了进度通知,但我在文件下载后收到了所有进度通知。

好像这个区域在 AFNetworking 2.0 中还是有点问题

有什么想法吗?

【问题讨论】:

  • 同意。我什至无法让下载器正常工作。它立即取消。
  • 如果你想兼容 iOS6/7,你应该使用AFHTTPRequestOperationAFURLSessionManager 仅适用于 iOS 7。我已经 posted an answer 展示了如何使用 AFHTTPRequestOperation 获取下载进度。

标签: ios objective-c afnetworking-2


【解决方案1】:

对于带有进度状态的下载文件,请使用此代码

 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSURL *URL = [NSURL URLWithString:@"http://..."];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];


NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress)
{
    NSLog(@"Progress: %f", downloadProgress.fractionCompleted);
    if (progressBlock) {
                    progressBlock(downloadProgress);
                }
} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
{
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
{
    if (response && successBlock) {
                    successBlock(response,filePath);
                }
    NSLog(@"File downloaded to: %@", filePath);
}];

[downloadTask resume];

【讨论】:

    【解决方案2】:

    Swift 的简单解决方案:

    let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
    let sessionManager = AFURLSessionManager(sessionConfiguration: sessionConfiguration)
    let request = NSURLRequest(URL: url)
    let sessionDownloadTask = sessionManager.downloadTaskWithRequest(request, progress: nil, destination: { (url, response) -> NSURL in
    
                return destinationPath.URLByAppendingPathComponent(fileName) //this is destinationPath for downloaded file
    
                }, completionHandler: { response, url, error in
    
                    //do sth when it finishes
            })
    

    现在你有两个选择:

    1. 使用UIProgressViewsetProgressWithDownloadProgressOfTask:

      progressView.setProgressWithDownloadProgressOfTask(sessionDownloadTask, animated: true)
      
    2. 使用AFURLSessionManagersetDownloadTaskDidWriteDataBlock:

      sessionManager.setDownloadTaskDidWriteDataBlock { session, sessionDownloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
      
          let progress = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)
              //do sth with current progress
      }
      

    最后不要忘记:

    sessionDownloadTask.resume()
    

    【讨论】:

      【解决方案3】:

      您应该使用 KVO 观察 NSProgress 对象的 fractionCompleted 属性:

      NSURL *url = [NSURL URLWithString:@"http://www.hfrmovies.com/TheHobbitDesolationOfSmaug48fps.mp4"];
      NSURLRequest *request = [NSURLRequest requestWithURL:url];
      AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
      NSProgress *progress;
      NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
          // …
      } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
          [progress removeObserver:self forKeyPath:@"fractionCompleted" context:NULL];
          // …
      }];
      
      [downloadTask resume];
      [progress addObserver:self
                  forKeyPath:@"fractionCompleted"
                     options:NSKeyValueObservingOptionNew
                     context:NULL];
      

      然后添加观察者方法:

      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
      {
          if ([keyPath isEqualToString:@"fractionCompleted"]) {
              NSProgress *progress = (NSProgress *)object;
              NSLog(@"Progress… %f", progress.fractionCompleted);
          } else {
              [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
          }
      }
      

      当然,您应该检查keyPath 和/或object 参数以确定这是否是您要观察的对象/属性。

      您还可以使用AFURLSessionManagerAFHTTPSessionManager 继承自该方法)的setDownloadTaskDidWriteDataBlock: 方法来设置接收下载进度更新的块。

      [session setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
          NSLog(@"Progress… %lld", totalBytesWritten);
      }];
      

      这种 AFNetworking 方法将 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 方法从 NSURLSessionDownloadDelegate 协议映射到更方便的阻塞机制。

      顺便说一句,Apple 的 KVO 实现被严重破坏。我建议使用更好的实现,例如 Mike Ash 提出的MAKVONotificationCenter。如果您有兴趣了解为什么 Apple 的 KVO 被破坏,请阅读 Mike Ash 的 Key-Value Observing Done Right

      【讨论】:

      • 由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'-[__NSCFBackgroundDownloadTask fractionCompleted]:无法识别的选择器发送到实例 0xd888b40'
      • fractionCompleted 属性属于NSProgress 对象。
      • 请注意,在 KVO 中,如果您处理通知,则不应调用 super。
      • 谢谢@alejandromp。我刚刚编辑了它。顺便说一句,我建议像 Mike Ash 提出的那样更好地实现 KVO:github.com/mikeash/MAKVONotificationCenter
      • 一般来说,你可以使用模式 NSStringFromSelector(@selector(fractionCompleted)) 而不是使用像@"fractionCompleted" 这样的编译器不透明字符串。
      【解决方案4】:

      我遇到了类似的问题,并找到了解决方案。

      查看以下链接: http://cocoadocs.org/docsets/AFNetworking/2.0.1/Categories/UIProgressView+AFNetworking.html

      #import <AFNetworking/UIKit+AFNetworking.h>
      

      并使用 UIProgressView 可用的其他方法

      setProgressWithDownloadProgressOfTask:animated:

      我是怎么做到的:

      NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request  progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response){
          NSURL *documentsDirectoryPath = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) firstObject]];
          return [documentsDirectoryPath URLByAppendingPathComponent:[targetPath lastPathComponent]];
      } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error){
          NSLog(@"File downloaded to: %@", filePath);
      
      }];
      
      [self.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
      [downloadTask resume];
      

      【讨论】:

      • 注意这段代码 sn-p 中的 manager 变量是 AFURLSessionManager 的一个实例可能会有所帮助。
      • 很棒的例子!谢谢!
      猜你喜欢
      • 1970-01-01
      • 2023-03-13
      • 2016-02-19
      • 1970-01-01
      • 1970-01-01
      • 2016-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多