使用NSURLSessionDataTask显示文件下载进度

29
我想显示特定文件下载进度(接收了多少字节)。使用 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是否可能?

4个回答

36

您需要实现以下代理:

<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;
}

非常感谢您的回复,但现在问题是当我下载文件时,这两种方法都没有被调用。我已经添加了委托并使用了上面相同的代码。 - Kiran Patel
我已经发布了另一个答案,请查看。 - Sishu
@KiranPatel 我已经修改了代码,请看一下并让我知道是否有效。谢谢 :) - Sishu
哇!太棒了@ Bullet Raja,非常感谢你在几分钟内解决了我的问题。我自己尝试了两天才能达到这个目标。再次感谢。它像魅力一样运作...... - Kiran Patel
我不确定。首先,他们在文档中声明:“由于数据可能是不连续的,因此您应该使用[NSData enumerateByteRangesUsingBlock:]来访问它。”其次,缺少如何知道下载已完成的信息。 - Nat

8
您可以使用NSURLSessionDownloadTask进行如下操作。调用startDownload方法。在.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);
}

2
以上代码完美运行,谢谢。但我想使用NSURLSessionDataTask来实现相同的功能,而不是NSURLSessionDownloadTask。这可行吗? - Kiran Patel
当然,没问题,请慢慢来。 - Kiran Patel

5

自从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


有没有可能包括Objective-C的示例? - Ser Pounce

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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接