iOS 文件下載Download,支持?jǐn)帱c(diǎn)續(xù)傳、后臺(tái)下載、設(shè)置下載并發(fā)數(shù)

Download.gif
下載功能的實(shí)現(xiàn):

使用的網(wǎng)絡(luò)連接的類為URLSession,在初始化URLSession前,需要先創(chuàng)建URLSessionConfiguration,可以理解為是URLSession需要的一個(gè)配置。URLSessionConfiguration有三種模式:

  1. default:可以使用緩存的Cache、Cookie、鑒權(quán)。
  2. ephemeral,僅內(nèi)存緩存,不使用緩存的Cache、Cookie、鑒權(quán)。
  3. background,支持后臺(tái)傳輸,需要一個(gè)identifier標(biāo)識(shí),用來重新連接session對(duì)象。

創(chuàng)建URLSession,設(shè)置配信息、代理、代理線程:

private lazy var session: URLSession = {
     let configuration = URLSessionConfiguration.background(withIdentifier: "DownloadBackgroundSessionIdentifier")
     let queue = OperationQueue()
     queue.maxConcurrentOperationCount = 1
     let session = URLSession(configuration: configuration, delegate: self, delegateQueue: queue)
     return session
}()

在實(shí)現(xiàn)下載前,還需要了解一個(gè)很重要的類,URLSessionTask,無論下載多少文件,我們只需要初始化一個(gè)URLSession即可,而每個(gè)task對(duì)應(yīng)一個(gè)任務(wù),需要通過task才能實(shí)現(xiàn)下載,URLSessionTask是一個(gè)基類,有四個(gè)子類:

1、URLSessionDataTask:下載時(shí),內(nèi)容以Data對(duì)象返回,需要我們不斷寫入文件

2、URLSessionUploadTask:繼承自URLSessionDataTask,內(nèi)容以Data對(duì)象返回,協(xié)議方法中可以查看請(qǐng)求時(shí)上傳內(nèi)容的過程

3、URLSessionStreamTask::建立了一個(gè)TCP/IP連接,替代InputStream/OutputStream,新的API可異步讀寫,自動(dòng)通過HTTP代理連接遠(yuǎn)程服務(wù)器

4、URLSessionDownloadTask:資源會(huì)下載到一個(gè)臨時(shí)文件,下載完成需將文件移動(dòng)至想要的路徑,系統(tǒng)會(huì)刪除臨時(shí)路勁文件,暫停時(shí),系統(tǒng)會(huì)返回NSData對(duì)象,恢復(fù)下載時(shí)用這個(gè)data創(chuàng)建task

Download 是通過URLSessionDataTask進(jìn)行下載的,核心代碼:

// 創(chuàng)建流
let stream = OutputStream(toFileAtPath: path(url: url), append: true)
// 創(chuàng)建請(qǐng)求
var request = URLRequest(url: URL(string: url)!)
// 忽略本地緩存,代理服務(wù)器以及其他中介,直接請(qǐng)求源服務(wù)端
request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
// 設(shè)置請(qǐng)求頭
request.setValue("bytes=\(getDownloadSize(url: url))-", forHTTPHeaderField: "Range")
// 創(chuàng)建一個(gè)Data任務(wù)
let task = session.dataTask(with: request)
let taskIdentifier = arc4random() % ((arc4random() % 10000 + arc4random() % 10000))
task.setValue(taskIdentifier, forKey: "taskIdentifier")
// 開啟下載
task.resume()

/// URLSessionDataDelegate
/// 接收到響應(yīng)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
      guard let model = sessionModels["\(dataTask.taskIdentifier)"],
            let stream = model.stream,
            let url = model.model.url else { return }
        
      // 打開流
      stream.open()
      // 接收這個(gè)請(qǐng)求,允許接收服務(wù)器的數(shù)據(jù)
      completionHandler(.allow)
}

/// 接收到服務(wù)器返回的數(shù)據(jù),會(huì)被調(diào)用多次
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
      guard let model = sessionModels["\(dataTask.taskIdentifier)"],
            let stream = model.stream,
            let url = model.model.url else { return }

      let bytes = [UInt8](data)
      // 寫入數(shù)據(jù)
      stream.write(UnsafePointer<UInt8>(bytes), maxLength: data.count)
      // 已下載大小
      let receivedSize = getDownloadSize(url: url)
      // 總大小 
      let expectedSize = model.model.totalLength
      // 下載進(jìn)度
      let progress: Double = Double(receivedSize) / Double(expectedSize)
}

/// URLSessionTaskDelegate
// 當(dāng)請(qǐng)求完成之后調(diào)用,如果錯(cuò)誤,那么error有值
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        
      guard let model = sessionModels["\(task.taskIdentifier)"],
            let url = model.model.url,
            url.dw_isURL else { return }
        
      if let error = error {
            debugPrint("下載失敗")
      } else {
            debugPrint("下載完成")
      }
        
      // 關(guān)閉流
      model.stream?.close()
      model.stream = nil
}

/// URLSessionDelegate
/// 應(yīng)用處于后臺(tái),所有下載任務(wù)完成及URLSession協(xié)議調(diào)用之后調(diào)用
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {}

Download API:
/// 設(shè)置下載并發(fā)數(shù), 默認(rèn)3
DownloadManager.default.maxDownloadCount = 3
/// 開啟下載
func download(model: DownloadModel)
/// 判斷該文件是否下載完成
func isCompletion(url: String) -> Bool
/// 判斷該文件是否存在
func isExistence(url: String) -> Bool
/// 根據(jù)url取消/暫停任務(wù)
func cancelTask(url: String)
/// 取消/暫停所有任務(wù)
func cancelAllTask()
/// 根據(jù)url刪除資源
func deleteFile(url: String)
/// 清空所有下載資源
func deleteAllFile()
/// 獲取下載的數(shù)據(jù)
func getDownloadModels() -> [DownloadModel]
/// 獲取下載完成的數(shù)據(jù)
func getDownloadFinishModels() -> [DownloadModel]
/// 獲取未下載完成的數(shù)據(jù)
func getDownloadingModel() -> [DownloadModel]
/// 將未完成的下載狀態(tài)改為.suspended
func updateDownloadingStateWithSuspended()
/// 開啟未完成的下載
func updateDownloading()
/// 獲取下載完成的文件路徑
func getFile(url: String) -> String
/// 獲取總緩存大小 單位:字節(jié)
func getCacheSize() -> Double
使用:見demo

Demo地址: Download

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評(píng)論 1 282
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評(píng)論 2 372

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