URLSession 下載任務(wù)暫停和繼續(xù)

下載任務(wù)的暫停和繼續(xù)在網(wǎng)絡(luò)請求中是一個(gè)常見需求,尤其在處理大文件或者長時(shí)間下載時(shí)。在 iOS 中,URLSession 提供了支持暫停和繼續(xù)下載的機(jī)制,稱為 后臺(tái)下載任務(wù)

雖然上述的線程池設(shè)計(jì)可以用來調(diào)度任務(wù)并控制任務(wù)的暫停/繼續(xù),但網(wǎng)絡(luò)請求的暫停和繼續(xù)需要結(jié)合 URLSessiondownloadTask(with:)resume() / suspend() 方法來實(shí)現(xiàn)。

我們可以使用 URLSession 來創(chuàng)建一個(gè)下載任務(wù),利用其支持的暫停和恢復(fù)功能,來實(shí)現(xiàn)下載任務(wù)的暫停和繼續(xù)。

1. 使用 URLSession 實(shí)現(xiàn)下載暫停和繼續(xù)

要支持下載任務(wù)的暫停和繼續(xù),我們需要:

  • 創(chuàng)建一個(gè) URLSessionDownloadTask
  • 使用 suspend() 方法暫停下載,使用 resume() 方法繼續(xù)下載。
  • 維護(hù)任務(wù)的狀態(tài),比如當(dāng)前的下載進(jìn)度,以便在恢復(fù)下載時(shí)可以從上次暫停的地方繼續(xù)。

2. 示例實(shí)現(xiàn)

下面是一個(gè)基于 URLSession 的下載器實(shí)現(xiàn),可以暫停、繼續(xù)和取消下載任務(wù):

import Foundation

/// 下載器
class ImageDownloader: NSObject, URLSessionDownloadDelegate {
    
    private var downloadTask: URLSessionDownloadTask?
    private var urlSession: URLSession!
    private var resumeData: Data? // 用于恢復(fù)下載的數(shù)據(jù)
    private var isDownloading = false
    private var taskId: UUID?
    
    override init() {
        super.init()
        
        // 創(chuàng)建 URLSession 配置并設(shè)置下載代理
        let configuration = URLSessionConfiguration.default
        urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
    }
    
    /// 開始下載圖片
    func startDownload(from url: URL, completion: @escaping (UIImage?) -> Void) -> UUID {
        let taskId = UUID() // 為每個(gè)任務(wù)生成唯一的 ID
        self.taskId = taskId
        
        // 創(chuàng)建下載任務(wù)
        downloadTask = urlSession.downloadTask(with: url)
        
        // 開始下載
        downloadTask?.resume()
        isDownloading = true
        
        return taskId
    }
    
    /// 暫停下載
    func pauseDownload() {
        downloadTask?.suspend()
        isDownloading = false
    }
    
    /// 繼續(xù)下載
    func resumeDownload() {
        if let resumeData = resumeData {
            // 恢復(fù)下載任務(wù)
            downloadTask = urlSession.downloadTask(withResumeData: resumeData)
            downloadTask?.resume()
            isDownloading = true
        } else {
            // 如果沒有保存的暫停數(shù)據(jù),重新開始下載
            downloadTask?.resume()
            isDownloading = true
        }
    }
    
    /// 取消下載
    func cancelDownload() {
        downloadTask?.cancel()
        isDownloading = false
    }
    
    // URLSessionDownloadDelegate 方法:處理下載進(jìn)度和完成
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        guard let taskId = taskId else { return }
        // 下載完成后的處理
        DispatchQueue.main.async {
            if let data = try? Data(contentsOf: location), let image = UIImage(data: data) {
                print("Download completed for task \(taskId)")
                // 調(diào)用 UI 更新代碼
            } else {
                print("Download failed for task \(taskId)")
            }
        }
    }
    
    // 下載進(jìn)度更新
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        print("Download progress: \(progress * 100)%")
    }
    
    // 下載暫停/恢復(fù)
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
        print("Download resumed at offset \(fileOffset) of total \(expectedTotalBytes) bytes")
    }
    
    // 保存暫停數(shù)據(jù)
    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive resumeData: Data) {
        self.resumeData = resumeData
    }
}

3. 使用示例

let imageDownloader = ImageDownloader()

// 圖片 URL 示例
let url = URL(string: "https://example.com/largeimage.jpg")!

// 開始下載并獲取任務(wù) ID
let taskId = imageDownloader.startDownload(from: url) { image in
    if let image = image {
        print("Image downloaded successfully!")
    } else {
        print("Failed to download image.")
    }
}

// 暫停下載
imageDownloader.pauseDownload()

// 繼續(xù)下載
imageDownloader.resumeDownload()

// 取消下載
imageDownloader.cancelDownload()

關(guān)鍵點(diǎn):

  1. 暫停和恢復(fù):通過 URLSessionDownloadTasksuspend()resume() 方法,可以控制下載任務(wù)的暫停和恢復(fù)。resumeData 用于存儲(chǔ)暫停點(diǎn)的下載數(shù)據(jù),并且可以通過 downloadTask(withResumeData:) 恢復(fù)下載任務(wù)。
  2. 下載進(jìn)度URLSessionDownloadTask 提供了進(jìn)度回調(diào)方法 didWriteData,我們可以通過 totalBytesWrittentotalBytesExpectedToWrite 來計(jì)算下載進(jìn)度,并顯示給用戶。
  3. 任務(wù)管理:每個(gè)任務(wù)都有唯一的 taskId,用于標(biāo)識(shí)任務(wù),以便在后續(xù)操作(暫停、繼續(xù)、取消)時(shí)使用。

4. 關(guān)于暫停和繼續(xù)下載的注意事項(xiàng):

  • 暫停和恢復(fù)時(shí)的恢復(fù)數(shù)據(jù)URLSessionDownloadTask 在暫停后會(huì)自動(dòng)生成一個(gè)恢復(fù)數(shù)據(jù)(resumeData),這個(gè)數(shù)據(jù)是下載文件的斷點(diǎn)信息。你需要存儲(chǔ)這個(gè)數(shù)據(jù),并在恢復(fù)時(shí)重新使用 downloadTask(withResumeData:) 方法。
  • 恢復(fù)點(diǎn)的有效性:并非所有下載任務(wù)都能順利恢復(fù)。只有部分下載任務(wù)(比如通過 HTTP 協(xié)議下載)支持?jǐn)帱c(diǎn)續(xù)傳,對于不支持的協(xié)議或任務(wù),可能需要重新開始下載。
  • 取消下載:通過 cancel() 方法,可以取消下載任務(wù)。取消后不會(huì)生成恢復(fù)數(shù)據(jù),如果需要恢復(fù),必須重新開始下載。

總結(jié):

通過 URLSessionDownloadTask 的暫停和繼續(xù)機(jī)制,我們可以很方便地在 iOS 中實(shí)現(xiàn)下載任務(wù)的暫停、繼續(xù)和取消功能。使用 suspend()resume() 方法結(jié)合 resumeData,可以在下載中斷后從上次停止的位置恢復(fù)下載,提供更好的用戶體驗(yàn)。

結(jié)合《Swift 實(shí)現(xiàn):三級(jí)下載隊(duì)列》,是否可以實(shí)現(xiàn)優(yōu)雅一點(diǎn)的預(yù)下載呢?
http://www.lxweimin.com/p/82e88ffb1e67

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

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