使用 NSURLSessionDataTask 显示文件下载进度

Posted

技术标签:

【中文标题】使用 NSURLSessionDataTask 显示文件下载进度【英文标题】:Showing the file download progress with 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 吗?

【问题讨论】:

【参考方案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(前 NSURLSessionTask)adopted 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()

【讨论】:

以上是关于使用 NSURLSessionDataTask 显示文件下载进度的主要内容,如果未能解决你的问题,请参考以下文章

什么时候使用 NSURLSessionDownloadTask 和 NSURLSessionDataTask?

NSURLSessionDataTask 内存警告应用程序崩溃

检查 NSURLSessionDataTask 的响应值

使用NSURLSessionDataTask实现大文件离线断点下载(完整)

如何从 NSURLSessionDataTask 完成处理程序返回 NSData

非唯一 NSURLSessionDataTask taskIdentifiers