下載功能的實(shí)現(xiàn):
使用的網(wǎng)絡(luò)連接的類為URLSession,在初始化URLSession前,需要先創(chuàng)建URLSessionConfiguration,可以理解為是URLSession需要的一個(gè)配置。URLSessionConfiguration有三種模式:
- default:可以使用緩存的Cache、Cookie、鑒權(quán)。
- ephemeral,僅內(nèi)存緩存,不使用緩存的Cache、Cookie、鑒權(quán)。
- 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