NSURLSession的下载和断点继传以及后台下载功能

来源:互联网 发布:使用excel制作软件 编辑:程序博客网 时间:2024/05/19 12:25

一.大体步骤

NSURLSession始于ios7.它具有访问接口,上传/下载数据,断点继传和后台下载等功能: 其使用步骤:

 1. 创建session指定其configuration 2. 由session执行任务得到task 3. task调用resume,启动网络请求

二.task分类

session的任务有四种:

 1. 数据任务 Data task 2. 下载任务 Download task 3. 上传任务 Upload task 4. 流任务 Stream task ios9之后出现的,用于TCP/IP流

三. configuration类型

configuration的类型有三种:

 1. 默认配置 Default sessions:    使用磁盘缓存,用将证书存在用户的钥匙串 2. 及时配置 Ephemeral sessions:    不使用磁盘缓存,也存储证书,它的信息存于RAM中,如果session被invalidate,这些信息也被清理掉 3. 后台配置 Background sessions:     配置上同默认配置,但是有一个独立进程来操作上传/下载

四.session生成task方式

对于生成每种task的方法,共有4种方式,举downloadTask为例子

1.用urlrequest请求

public func downloadTaskWithRequest(request: NSURLRequest) ->   NSURLSessionDownloadTask 

2.用url请求

public func downloadTaskWithURL(url: NSURL) -> NSURLSessionDownloadTask

3.带handler的URLRequest请求,注意,如果写了handler,就不会进入代理方法,即使设置了代理也没用

public func downloadTaskWithRequest(request: NSURLRequest, completionHandler: (NSURL?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDownloadTask

4.带handler的URL请求

public func downloadTaskWithURL(url: NSURL, completionHandler: (NSURL?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDownloadTask

五.task分类讲解

下面讲解每种task的具体使用方法

NSURLSession的代理继承关系如图:
NSURLSessionDelegate
|
NSURLSessionTaskDelegate
| | |
NSURLSessionDataDelegate NSURLSessionDownloadDelegate NSURLSessionStreamDelegate

1. 数据任务 Data task

使用:

let config = NSURLSessionConfiguration.defaultSessionConfiguration()let url = NSURL(string:datadUrlNeighbor)task.resume()

进入代理方法顺序:

1.首先进入NSURLSessionDataDelegate的didReceiveResponse方法.我们要手动在这里调用completionHandler(.Allow).系统才会继续进入下一步的代理方法,否则到此就结束了

func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {//这个方法,只有在sessiontaskdatatask的时候才会进入 completionHandler(.Allow)}

2.然后进入didReceiveData方法,获得json数据,可以做业务操作

func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {    print("did receive data")    let dic = try? NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)    print("get dic:\(dic!)")}

3.然后进入didCompleteWithError方法,它是NSURLSessionTaskDelegate的方法,表示请求结束了

func URLSession(session: NSURLSession, task: NSURLSessionTask,  error: NSError?) {    print("did complete")}

2. 下载任务 Download task

使用:

let config = NSURLSessionConfiguration.defaultSessionConfiguration()let session = NSURLSession(configuration: config,delegate: self, delegateQueue: NSOperationQueue.mainQueue())//方式一 :不使用handler 会进入代理方法let url = NSURL(string:datadUrlNeighbor)let downloadTask = session.downloadTaskWithURL(url!)downloadTask.resume()

进入代理方法顺序:

1.NSURLSessionDownloadDelegate的didWriteData,数据正在写入沙盒的tmp文件夹,就会调用这个方法.它是分批次写入的,每写入一段数据就调用这个方法一次,所以会被多次调用

参数解释:bytesWritten是本次写入的数据长度totalBytesWritten是已经写在磁盘上的长度totalBytesExpectedToWrite是数据本来的长度
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {    print("didwrite:\(bytesWritten), \(totalBytesWritten), \(totalBytesWritten), \(totalBytesExpectedToWrite)")}

2.NSURLSessionDownloadDelegate的didFinishDownloadingToURL方法,当数据下载完成后,此时的文件是一个以tmp结尾的文件,命名类似于:
tmp文件

3.进入这个方法didFinishDownloadingToURL.在这里必须执对tmp文件的转移处理,否则当除了这个方法后,tmp文件就被删除了.

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {    print("download to url:\(location)")    self.moveToCache(location, name: "ivy.zip")    self.tintLabel.text = "download to url"}

4.进入didCompleteWithError方法

func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?){}

三. 上传

由于上传本人研究不深入,暂时不写,以后更新本节

四. 断点继传

使用:

注意:1. 这里和下载的区别是:task设置为成员变量了,因为在中断的时候,需要这个task来调用cancelByProducingResumeData2. session也设置为了成员变量,因为在继传的时候,需要用这个session来调用downloadTaskWithResumeData,开启一个新的downloadtask
//经测试这里写backgroundconfig和defaultconfig都可以//let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)let config = NSURLSessionConfiguration.defaultSessionConfiguration()downloadSession = NSURLSession(configuration: config,delegate: self, delegateQueue: NSOperationQueue.mainQueue())let url = NSURL(string:downloadUrlNeighbor)downloadSessionTask = downloadSession!.downloadTaskWithURL(url!)downloadSessionTask!.resume()

顺序:

1. 按下中止按钮:

在回调块中,保存  self.resumeData,便于在继传时使用
downloadSessionTask?.cancelByProducingResumeData({ (data:NSData?) in    self.resumeData = data    self.downloadSessionTask = nil //downloadSessionTask已经没用了 要置为nil,因为下次继传时会由session新开一个task})

如果不按下中止按钮,它进入代理方法的顺序和正常下载是完全一样的.即先进入didWriteData,然后进入didFinishDownloadingToURL,最后didCompleteWithError

如果按下中止按钮,会发生:

1. 进入didWriteData,毕竟也是写了一些数据的2. 会进cancelByProducingResumeData的回调,在回调里,我们要记录下resumedata,这是继传时要传入的参数,还要设置成员downloadSessionTask为nil,因为下次的继传会由session创建一个新的task,通过调用downloadTaskWithResumeData3. 进入代理didCompleteWithError方法

2. 继传

比如我们用一个按钮来启动继传,其中的代码如下:
就是保存的成员变量task 调用downloadTaskWithResumeData方法

@IBAction func clickGoOn(sender: AnyObject) {    guard self.resumeData != nil else{        return     }     //这样写可以进入代理:1 didFinishDownloadingToURL 2 didCompleteWithError    downloadSessionTask = downloadSession?.downloadTaskWithResumeData(self.resumeData!)    downloadSessionTask?.resume()}

由于downloadTaskWithResumeData也有2种方式,带handler和不带handler的,如果调用了带有handler的那个,则不进入代理方法

进入代理方法的顺序:
1.didResumeAtOffset

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {    print("didresume:\(fileOffset),total:\(expectedTotalBytes)")}

2.didWriteData 又开始写入磁盘了

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {    print("didwrite:\(bytesWritten), \(totalBytesWritten), \(totalBytesExpectedToWrite)")}

3.didFinishDownloadingToURL 这个比较重要,一次下载可以多次中止,但是只有全部写入成功后才会进入这个代理, 在这里将 下载的文件转移走,不然出了这个方法会被删除的

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {    print("download to url:\(location)")    self.resumeUrl = location    self.moveToCache(self.resumeUrl, name: "ivy.zip")    self.tintLabel.text = "download to url"    }

4.didCompleteWithError 这回是真的下载完成了

 func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?){    print("did complete")}

四. 后台下载

在下载过程中,如果按home键将app切换到后台,只要不杀死程序,session还能保持其下载能力,完成后,通知到appdelegate的handleEventsForBackgroundURLSession方法.
.虽然app不会因此回到前端

注意:    1. 后台config必须使用backgroundSessionConfigurationWithIdentifier,要传入一个唯一的标识符    2. 有几个下载任务就要创建几个config和session.每个任务都需要一个独立标示的config,以及session    3. session必须设置delegate    4. 只支持HTTP/HTTPS模式    5. 只支持从文件上传,不支持从data上传(也就是只能用这个函数:func uploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionUploadTask    而不能用这个函数:func uploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionUploadTask)    6. 请用真机调试,模拟器按下home键之后不会有效果,而是会一直在delegate里下载直到完成为止

使用:

let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)let session = NSURLSession(configuration:config, delegate:self, delegateQueue: NSOperationQueue.mainQueue())let url = NSURL(string:downloadUrlNeighbor)let task = session.downloadTaskWithURL(url!)task.resume()

进入代理顺序:
1.开始下载时
NSURLSessionDownloadDelegate代理的didWriteData方法

2.按下home,APP进入后台
代理的didWriteData方法不再被进入,而是程序后台静默下载

3.下载完成后

第一步:进入AppDelegate的方法:

func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {    //self.downloadCompletionHandler = completionHandler    print("----application hadle event")    let config = NSURLSessionConfiguration.backgroundSessionConfiguration(identifier)    //The new session is automatically reassociated with ongoing background activity.    //这个session被自动绑定到了后台运行的app    let session = NSURLSession(configuration: config , delegate:self.mySessionDelegate, delegateQueue:NSOperationQueue.mainQueue())        //去使用的类里面注册一个handler, 直接传过去也可以的,其实self.window.rootViewController = xxx也可以的                                       self.mySessionDelegate.addCompletionHandler(completionHandler, identifier: identifier)}
解释:      1. 保存handler的方式是多种的, 可以给AppDelegate设置一个dictionary的属性.也可以传入session的delegate的dictionary属性,我选择了后者     2. 要创建一个session,文档说这个session会被自动绑定到后台的activity

第二步:

进入NSURLSessionDownloadDelegate的didFinishDownloadingToURL方法,请在这里 搬运下载好的tmp文件

第三步:进入didCompleteWithError

第四步:进入NSURLSessionDelegate的URLSessionDidFinishEventsForBackgroundURLSession方法

func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession){    print("did finish events")    if (session.configuration.identifier != nil) {    let handler = self.completionHandlerDictionary![session.configuration.identifier!]    guard handler != nil else {    return}    handler!()    //移除 dictionary中的数据                    self.completionHandlerDictionary?.removeValueForKey(session.configuration.identifier!)    self.tintLabel.text = "finish event"    }}

在第三步或第四步里面, 把从appdelegate里面获取到的handler执行一下,这么做的目的,文档告诉我们是为了让操作系统知道程序可以被继续安全的挂起.

参考文档:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html#//apple_ref/doc/uid/TP40013509-SW44

demo: https://github.com/ivychenyucong/TestNSURLSession

ps:打算翻译下那篇参考文档 含金量挺高的

1 0
原创粉丝点击