NSURLSession的下載和斷點繼傳和后臺下載

一.概述

NSURLSession始于ios7.它具有訪問接口,上傳/下載數(shù)據(jù),斷點繼傳和后臺下載等功能:其使用步驟:

1.創(chuàng)建session指定其configuration

2.由session執(zhí)行任務(wù)得到task

3. task調(diào)用resume,啟動網(wǎng)絡(luò)請求

1.task分類

session的任務(wù)有四種:

1.數(shù)據(jù)任務(wù)Data task

2.下載任務(wù)Download task

3.上傳任務(wù)Upload task

4.流任務(wù)Stream task ios9之后出現(xiàn)的,用于TCP/IP流

2. configuration類型

configuration的類型有三種:

1.默認(rèn)配置Defaultsessions:

使用磁盤緩存,用將證書存在用戶的鑰匙串

2.即時配置Ephemeralsessions:

不使用磁盤緩存,也存儲證書,它的信息存于RAM中,如果session被invalidate,這些信息也被清理掉

3.后臺配置Background sessions:

配置上同默認(rèn)配置,但是有一個獨立進(jìn)程來操作上傳/下載

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,就不會進(jìn)入代理方法,即使設(shè)置了代理也沒用:

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的代理繼承關(guān)系如圖:2-1


2-1


1.數(shù)據(jù)任務(wù)Data task

使用:

letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()

leturl = NSURL(string:datadUrlNeighbor)

task.resume()

進(jìn)入代理方法順序:

1.首先進(jìn)入NSURLSessionDataDelegate的URLSession:dataTask:didReceiveResponse:completionHandler方法.我們要手動在這里調(diào)用completionHandler(.Allow).系統(tǒng)才會繼續(xù)進(jìn)入下一步的代理方法,否則到此就結(jié)束了

func URLSession(session: NSURLSession, dataTask:

NSURLSessionDataTask, didReceiveResponse response: NSURLResponse,

completionHandler: (NSURLSessionResponseDisposition) -> Void){

//這個方法,只有在session的task是datatask的時候才會進(jìn)入

completionHandler(.Allow)

}

2.然后進(jìn)入URLSession:dataTask:didReceiveData:方法,獲得json數(shù)據(jù),可以做業(yè)務(wù)操作

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

print("did receivedata")

let dic = try?NSJSONSerialization.JSONObjectWithData(data,

options: .MutableContainers)

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

}

3.然后進(jìn)入URLSession:task:didCompleteWithError方法,它是NSURLSessionTaskDelegate的方法,表示請求結(jié)束了

funcURLSession(session:NSURLSession, task:NSURLSessionTask, didCompleteWithError

error:NSError?){

print("did complete")

}

2.下載任務(wù)Download task

使用:

letconfig =NSURLSessionConfiguration.defaultSessionConfiguration()

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

//方式一:不使用handler會進(jìn)入代理方法

leturl = NSURL(string:datadUrlNeighbor)

letdownloadTask = session.downloadTaskWithURL(url!)

downloadTask.resume()

進(jìn)入代理方法順序:

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

2.,數(shù)據(jù)正在寫入沙盒的tmp文件夾,就會調(diào)用這個方法.它是分批次寫入的,每寫入一段數(shù)據(jù)就調(diào)用這個方法一次,所以會被多次調(diào)用

參數(shù)解釋:

bytesWritten是本次寫入的數(shù)據(jù)長度

totalBytesWritten是已經(jīng)寫在磁盤上的長度

totalBytesExpectedToWrite是數(shù)據(jù)本來的長度

func URLSession(session: NSURLSession,

downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64,

totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){

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

}

2.NSURLSessionDownloadDelegate的URLSession:downloadTask:didFinishDownloadingToURL方法,當(dāng)數(shù)據(jù)下載完成后,此時的文件是一個以tmp結(jié)尾的文件,命名類似于:


tmp下載臨時文件


注意:進(jìn)入這個方法didFinishDownloadingToURL.在這里必須執(zhí)對tmp文件的轉(zhuǎn)移處理,否則當(dāng)除了這個方法后,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.進(jìn)入URLSession:downloadTask:didCompleteWithError方法

func URLSession(session:

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

}

3.上傳

由于上傳本人研究不深入,暫時不寫,以后更新本節(jié)

三.斷點繼傳

1.開始使用:

注意:

這里和前面下載的區(qū)別是:

1.task設(shè)置為成員變量了,因為在中斷的時候,需要這個task來調(diào)用cancelByProducingResumeData:

2.session也設(shè)置為了成員變量,因為在繼傳的時候,需要用這個session來調(diào)用downloadTaskWithResumeData,開啟一個新的downloadtask

//經(jīng)測試這里寫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()

執(zhí)行順序:

2.按下中止按鈕:

在回調(diào)塊中,保存self.resumeData,便于在繼傳時使用

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

self.resumeData=data

self.downloadSessionTask=nil//downloadSessionTask已經(jīng)沒用了要置為nil,因為下次繼傳時會由session新開一個task

})

如果不按下中止按鈕,它進(jìn)入代理方法的順序和正常下載是完全一樣的.即先進(jìn)入didWriteData,然后進(jìn)入didFinishDownloadingToURL,最后didCompleteWithError

如果按下中止按鈕,會發(fā)生:

1.進(jìn)入URLSession:downloadTask:didWriteData,畢竟也是寫了一些數(shù)據(jù)的

2.會進(jìn)cancelByProducingResumeData的回調(diào)塊,在回調(diào)塊里,我們要記錄下resumedata,這是繼傳時要傳入的參數(shù),還要設(shè)置成員downloadSessionTask為nil,因為下次的繼傳會由session創(chuàng)建一個新的task,通過調(diào)用downloadTaskWithResumeData

3.進(jìn)入代理URLSession:task:didCompleteWithError方法

3.繼傳

比如我們用一個按鈕來啟動繼傳,其中的代碼如下:

就是保存的成員變量task調(diào)用downloadTaskWithResumeData:方法

@IBAction func clickGoOn(sender:AnyObject) {

guardself.resumeData!=nilelse{

return

}

//這樣寫可以進(jìn)入代理:1 didFinishDownloadingToURL 2 didCompleteWithError

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

downloadSessionTask?.resume()

}

由于downloadTaskWithResumeData也有2種方式,帶handler和不帶handler的,如果調(diào)用了帶有handler的那個,則不進(jìn)入代理方法

進(jìn)入代理方法的順序:

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這個比較重要,一次下載可以多次中止,但是只有全部寫入成功后才會進(jìn)入這個代理,在這里將下載的文件轉(zhuǎn)移走,不然出了這個方法會被刪除的

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.進(jìn)入URLSession:task:didCompleteWithError這回是真的下載完成了

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

print("did complete")

}

四.后臺下載

在下載過程中,如果按home鍵將app切換到后臺,只要不殺死程序,session還能保持其下載能力,完成后,通知到appdelegate的handleEventsForBackgroundURLSession方法.,雖然app不會因此回到前端.

PS:據(jù)我所知,ios只允許后臺程序活躍10分鐘,(除了音頻,電話,地圖除外),而且還不定是連續(xù)的10分鐘,那么,NSURLSession的后臺下載是否包含在這10分鐘內(nèi)呢?我不是很好做測試啊.

注意:

1.后臺config必須使用backgroundSessionConfigurationWithIdentifier,要傳入一個唯一的標(biāo)識符

2.有幾個下載任務(wù)就要創(chuàng)建幾個config和session.每個任務(wù)都需要一個獨立標(biāo)示的config,以及session

3. session必須設(shè)置delegate

4.只支持HTTP/HTTPS模式

5.只支持從文件上傳,不支持從data上傳

(也就是只能用這個函數(shù):funcuploadTaskWithRequest(request: NSURLRequest, fromFile fileURL: NSURL,completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) ->NSURLSessionUploadTask

而不能用這個函數(shù):func uploadTaskWithRequest(request: NSURLRequest,fromFile fileURL: NSURL, completionHandler: (NSData?, NSURLResponse?, NSError?)-> Void) -> NSURLSessionUploadTask)

6.請用真機(jī)調(diào)試,模擬器按下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()

進(jìn)入代理順序:

1.開始下載時

進(jìn)入NSURLSessionDownloadDelegate代理的.URLSession:downloadTaskdidWriteData方法

2.按下home,APP進(jìn)入后臺

代理的.URLSession:downloadTaskdidWriteData方法不再被進(jìn)入,而是程序后臺靜默下載

3.下載完成后

第一步:進(jìn)入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設(shè)置一個dictionary的屬性.也可以傳入session的delegate的dictionary屬性,我選擇了后者

2.要創(chuàng)建一個session,文檔說這個session會被自動綁定到后臺的activity

第二步:

進(jìn)入NSURLSessionDownloadDelegate的didFinishDownloadingToURL方法,請在這里搬運下載好的tmp文件

第三步:進(jìn)入URLSession:task:didCompleteWithError

第四步:進(jìn)入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中的數(shù)據(jù)

self.completionHandlerDictionary?.removeValueForKey(session.configuration.identifier!)

self.tintLabel.text="finish event"

}

}

在第三步或第四步里面,把從appdelegate里面獲取到的handler執(zhí)行一下,這么做的目的,文檔告訴我們是為了讓操作系統(tǒng)知道程序可以被繼續(xù)安全的掛起.

參考文檔:

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:打算翻譯下那篇參考文檔含金量挺高的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抢蚀,一起剝皮案震驚了整個濱河市蹦漠,隨后出現(xiàn)的幾起案子狮崩,更是在濱河造成了極大的恐慌励翼,老刑警劉巖痪枫,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件败徊,死亡現(xiàn)場離奇詭異涧团,居然都是意外死亡簇抵,警方通過查閱死者的電腦和手機(jī)允瞧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門简软,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓷式,你說我怎么就攤上這事替饿。” “怎么了贸典?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵视卢,是天一觀的道長。 經(jīng)常有香客問我廊驼,道長据过,這世上最難降的妖魔是什么惋砂? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮绳锅,結(jié)果婚禮上西饵,老公的妹妹穿的比我還像新娘。我一直安慰自己鳞芙,他們只是感情好眷柔,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著原朝,像睡著了一般驯嘱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喳坠,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天鞠评,我揣著相機(jī)與錄音,去河邊找鬼壕鹉。 笑死剃幌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晾浴。 我是一名探鬼主播负乡,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怠肋!你這毒婦竟也來了敬鬓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤笙各,失蹤者是張志新(化名)和其女友劉穎钉答,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杈抢,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡数尿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惶楼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片右蹦。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖歼捐,靈堂內(nèi)的尸體忽然破棺而出何陆,到底是詐尸還是另有隱情,我是刑警寧澤豹储,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布贷盲,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏巩剖。R本人自食惡果不足惜铝穷,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望佳魔。 院中可真熱鬧,春花似錦鞠鲜、人聲如沸宁脊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庐氮。三九已至,卻和暖如春宋彼,著一層夾襖步出監(jiān)牢的瞬間弄砍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工输涕, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留音婶,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓莱坎,卻偏偏與公主長得像衣式,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檐什,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容