【问题标题】:Showing the file download progress with NSURLSessionDataTask使用 NSURLSessionDataTask 显示文件下载进度
【发布时间】:2014-06-02 05:13:46
【问题描述】:

我想显示特定文件的文件下载进度(收到多少字节)。它适用于 NSURLSessionDownloadTask 。我的问题是我想用 NSURLSessionDataTask 实现同样的效果。

这是接收文件到 NSData 并写入文件夹的代码:

NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];

NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:theRessourcesURL
    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{ 
       if(error == nil)
       {

            NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

               NSString *pathToDownloadTo = [NSString stringWithFormat:@"%@/%@", docsDir, Name];

               NSLog(@"SIZE : %@",[NSByteCountFormatter stringFromByteCount:data.length countStyle:NSByteCountFormatterCountStyleFile]);

               [data writeToFile:pathToDownloadTo options:NSDataWritingAtomic error:&error];
       }
}];

[dataTask resume];

写入或完成数据任务后(收到文件后)我正在获取文件大小:

NSLog(@"SIZE : %@",[NSByteCountFormatter stringFromByteCount:data.length countStyle:NSByteCountFormatterCountStyleFile]);

但我想显示它的当前字节接收状态,这可能与 NSURLSessionDataTask 吗?

【问题讨论】:

    标签: ios nsurlsession nsurlsessiondownloadtask nsurlsessionconfiguration


    【解决方案1】:

    您需要实现以下委托:

    <NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate>

    还需要创建两个属性:

    @property (nonatomic, retain) NSMutableData *dataToDownload;
    @property (nonatomic) float downloadSize;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
    
        NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
    
        NSURL *url = [NSURL URLWithString: @"your url"];
        NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithURL: url];
    
        [dataTask resume];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
        completionHandler(NSURLSessionResponseAllow);
    
        progressBar.progress=0.0f;
        _downloadSize=[response expectedContentLength];
        _dataToDownload=[[NSMutableData alloc]init];
    }
    
    - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
        [_dataToDownload appendData:data];
        progressBar.progress=[ _dataToDownload length ]/_downloadSize;
    }
    

    【讨论】:

    • 非常感谢您的重播,但现在的问题是当我下载文件时这两种方法都没有调用,我添加了委托并使用了相同的上述代码。
    • 我已经发布了另一个答案,请看。
    • @KiranPatel 我已经更改了代码,请看一下山雀,让我知道它是否有效。谢谢。:)
    • 哇!太棒了@ Bullet Raja,非常感谢你在小步舞曲中解决了我的问题,我从过去 2 天开始一​​直在努力实现这一目标。再次谢谢你。它就像魅力一样......
    • 我不确定。首先,他们在文档中声明“由于数据可能不连续,您应该使用 [NSData enumerateByteRangesUsingBlock:] 来访问它。”。其次,它缺少如何知道下载完成的信息。
    【解决方案2】:

    你也可以像下面这样使用 NSURLSessionDownloadTask。调用startDownload方法e.在.h文件中使用这个

    - (void)startDownload
    {
        NSString *s;
        s = @"http://www.nasa.gov/sites/default/files/styles/1600x1200_autoletterbox/public/pia17474_1.jpg?itok=4fyEwd02";
        NSURLSessionDownloadTask *task = [self.session downloadTaskWithURL:[NSURL URLWithString:s]];
        [task resume];
    }
    
    - (NSURLSession *) configureSession {
        NSURLSessionConfiguration *config =
        [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.neuburg.matt.ch37backgroundDownload"];
        config.allowsCellularAccess = NO;
        // ... could set config.discretionary here ...
        NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        return session;
    }
    
    -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
        CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
        NSLog(@"downloaded %d%%", (int)(100.0*prog));
    
    }
    
    -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
        // unused in this example
    }
    
    -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
        NSData *d = [NSData dataWithContentsOfURL:location];
        UIImage *im = [UIImage imageWithData:d];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.image = im;
    
        });
    }
    
    -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
        NSLog(@"completed; error: %@", error);
    }
    

    【讨论】:

    • 以上代码运行良好,谢谢。但我想用 NSURLSessionDataTask 而不是 NSURLSessionDownloadTask 来实现同样的功能。有可能吗?
    • 好的,没问题,请慢慢来。
    【解决方案3】:

    iOS 11.0 和 macOS 10.13 开始,URLSessionTask(前 NSURLSessionTaskadopted the ProgressReporting protocol。这意味着您可以使用progress 属性来跟踪会话任务的进度。

    如果您已经知道如何使用 KVO 观察者,您可以执行以下操作:

    task = session.downloadTask(with: url)
    task.resume()
    task.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: &self.progressKVOContext)
    

    并通过以下方式观察值:

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &self.progressKVOContext, let keyPath = keyPath {
            switch keyPath {
            case "fractionCompleted":
                guard let progress = object as? Progress else {
                    return
                }
                DispatchQueue.main.async { [weak self] in
                    self?.onDownloadProgress?(progress.fractionCompleted)
                }
            case "isCancelled":
                cancel()
            default:
                break
            }
        }
        else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
    

    iOS 13 更新

    OperationQueue 现在有一个progress property

    例如,UIProgressView 可以观察该属性并使用observedProgress 自动更新进度值。 https://nshipster.com/ios-13/#track-the-progress-of-enqueued-operations 中的完整示例。

    【讨论】:

    • 是否有可能包含 Objective-C 示例?
    【解决方案4】:
    import Foundation
    import PlaygroundSupport
    
    let page = PlaygroundPage.current
    page.needsIndefiniteExecution = true
    
    let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
    let task = URLSession.shared.dataTask(with: url) { _, _, _ in
      page.finishExecution()
    }
    
    // Don't forget to invalidate the observation when you don't need it anymore.
    let observation = task.progress.observe(\.fractionCompleted) { progress, _ in
      print(progress.fractionCompleted)
    }
    
    task.resume()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-30
      • 1970-01-01
      • 2017-01-28
      • 1970-01-01
      • 1970-01-01
      • 2016-12-30
      相关资源
      最近更新 更多