使用 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实现大文件离线断点下载(完整)