Отображение прогресса загрузки файла с помощью NSURLSessionDataTask
Я хочу показать ход загрузки файла (сколько байтов получено) определенного файла. Он отлично работает с 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];
Я получаю размер файла после записи или полной datatask (после получения файла):
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;
}
Ответ 2
Вы также можете использовать NSURLSessionDownloadTask, как показано ниже. Вызов startDownload methode.In.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);
}
Ответ 3
Поскольку iOS 11.0 и macOS 10.13, URLSessionTask
(бывший NSURLSessionTask
) принял протокол ProgressReporting. Это означает, что вы можете использовать свойство 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
.
Например, UIProgressView
может наблюдать это свойство и автоматически обновлять значение прогресса, используя observedProgress
. Полный пример в https://nshipster.com/ios-13/#track-the-progress-of-enqueued-operations.
Ответ 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()