NSURLSession的下载和断点继传和后台下载

一.概述

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

1.创建session指定其configuration

2.由session执行任务得到task

3. task调用resume,启动网络请求

1.task分类

session的任务有四种:

1.数据任务Data task

2.下载任务Download task

3.上传任务Upload task

4.流任务Stream task ios9之后出现的,用于TCP/IP流

2. configuration类型

configuration的类型有三种:

1.默认配置Defaultsessions:

使用磁盘缓存,用将证书存在用户的钥匙串

2.即时配置Ephemeralsessions:

不使用磁盘缓存,也存储证书,它的信息存于RAM中,如果session被invalidate,这些信息也被清理掉

3.后台配置Background sessions:

配置上同默认配置,但是有一个独立进程来操作上传/下载

3.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的代理继承关系如图:2-1


2-1


1.数据任务Data task

使用:

letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()

leturl = NSURL(string:datadUrlNeighbor)

task.resume()

进入代理方法顺序:

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

func URLSession(session: NSURLSession, dataTask:

NSURLSessionDataTask, didReceiveResponse response: NSURLResponse,

completionHandler: (NSURLSessionResponseDisposition) -> Void){

//这个方法,只有在session的task是datatask的时候才会进入

completionHandler(.Allow)

}

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

funcURLSession(session:NSURLSession, dataTask:NSURLSessionDataTask, didReceiveDatadata:NSData) {

print("did receivedata")

let dic = try?NSJSONSerialization.JSONObjectWithData(data,

options: .MutableContainers)

print("get dic:\(dic!)")

}

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

funcURLSession(session:NSURLSession, task:NSURLSessionTask, didCompleteWithError

error:NSError?){

print("did complete")

}

2.下载任务Download task

使用:

letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()

letsession = NSURLSession(configuration: config,delegate: self, delegateQueue:NSOperationQueue.mainQueue())

//方式一:不使用handler会进入代理方法

leturl = NSURL(string:datadUrlNeighbor)

letdownloadTask = session.downloadTaskWithURL(url!)

downloadTask.resume()

进入代理方法顺序:

1.NSURLSessionDownloadDelegate的URLSession:downloadTask:didWriteData:totalBytesWritten: totalBytesExpectedToWrite:

2.,数据正在写入沙盒的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的URLSession:downloadTask:didFinishDownloadingToURL方法,当数据下载完成后,此时的文件是一个以tmp结尾的文件,命名类似于:


tmp下载临时文件


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

func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURLlocation: NSURL) {

print("download to url:\(location)")

self.moveToCache(location, name:"ivy.zip")

self.tintLabel.text ="download to url"

}

3.进入URLSession:downloadTask:didCompleteWithError方法

func URLSession(session:

NSURLSession,task: NSURLSessionTask,didCompleteWithError error: NSError?){

}

3.上传

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

三.断点继传

1.开始使用:

注意:

这里和前面下载的区别是:

1.task设置为成员变量了,因为在中断的时候,需要这个task来调用cancelByProducingResumeData:

2.session也设置为了成员变量,因为在继传的时候,需要用这个session来调用downloadTaskWithResumeData,开启一个新的downloadtask

//经测试这里写backgroundconfig和defaultconfig都可以

//let config =

NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)

letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()

downloadSession =

NSURLSession(configuration: config,delegate:self, delegateQueue: NSOperationQueue.mainQueue())

leturl = NSURL(string:downloadUrlNeighbor)

downloadSessionTask =downloadSession!.downloadTaskWithURL(url!)

downloadSessionTask!.resume()

执行顺序:

2.按下中止按钮:

在回调块中,保存self.resumeData,便于在继传时使用

downloadSessionTask?.cancelByProducingResumeData({ (data:NSData?)in

self.resumeData=data

self.downloadSessionTask=nil//downloadSessionTask已经没用了要置为nil,因为下次继传时会由session新开一个task

})

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

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

1.进入URLSession:downloadTask:didWriteData,毕竟也是写了一些数据的

2.会进cancelByProducingResumeData的回调块,在回调块里,我们要记录下resumedata,这是继传时要传入的参数,还要设置成员downloadSessionTask为nil,因为下次的继传会由session创建一个新的task,通过调用downloadTaskWithResumeData

3.进入代理URLSession:task:didCompleteWithError方法

3.继传

比如我们用一个按钮来启动继传,其中的代码如下:

就是保存的成员变量task调用downloadTaskWithResumeData:方法

@IBAction func clickGoOn(sender:AnyObject) {

guardself.resumeData!=nilelse{

return

}

//这样写可以进入代理:1 didFinishDownloadingToURL 2 didCompleteWithError

downloadSessionTask = downloadSession?.downloadTaskWithResumeData(self.resumeData!)

downloadSessionTask?.resume()

}

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

进入代理方法的顺序:

1.URLSession:downloadTask:didResumeAtOffset

func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffsetfileOffset: Int64, expectedTotalBytes: Int64) {

print("didresume:\(fileOffset),total:\(expectedTotalBytes)")

}

2.URLSession:downloadTask:didWriteData又开始写入磁盘了

func URLSession(session:

NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData

bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite:

Int64){

print("didwrite:\(bytesWritten),\(totalBytesWritten),\(totalBytesExpectedToWrite)")

}

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

func URLSession(session:NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURLlocation: NSURL) {

print("download to url:\(location)")

self.resumeUrl = location

self.moveToCache(self.resumeUrl,

name:"ivy.zip")

self.tintLabel.text ="download to url"

}

4.进入URLSession:task:didCompleteWithError这回是真的下载完成了

func URLSession(session: NSURLSession, task:NSURLSessionTask, didCompleteWithErrorerror: NSError?){

print("did complete")

}

四.后台下载

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

PS:据我所知,ios只允许后台程序活跃10分钟,(除了音频,电话,地图除外),而且还不定是连续的10分钟,那么,NSURLSession的后台下载是否包含在这10分钟内呢?我不是很好做测试啊.

注意:

1.后台config必须使用backgroundSessionConfigurationWithIdentifier,要传入一个唯一的标识符

2.有几个下载任务就要创建几个config和session.每个任务都需要一个独立标示的config,以及session

3. session必须设置delegate

4.只支持HTTP/HTTPS模式

5.只支持从文件上传,不支持从data上传

(也就是只能用这个函数:funcuploadTaskWithRequest(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里下载直到完成为止

使用:

letconfig =NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(backgroundId)

letsession = NSURLSession(configuration:config,

delegate:self, delegateQueue:NSOperationQueue.mainQueue())

leturl = NSURL(string:downloadUrlNeighbor)

lettask=session.downloadTaskWithURL(url!)

task.resume()

进入代理顺序:

1.开始下载时

进入NSURLSessionDownloadDelegate代理的.URLSession:downloadTaskdidWriteData方法

2.按下home,APP进入后台

代理的.URLSession:downloadTaskdidWriteData方法不再被进入,而是程序后台静默下载

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 backgroundactivity.

//这个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文件

第三步:进入URLSession:task: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:打算翻译下那篇参考文档含金量挺高的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容