Alamofire(二)URLSession

Alamofire學習(一)網絡基礎swift

Alamofire(二)URLSessionapi

Alamofire(三)後臺下載原理數組

@TOC緩存

  • 最近在學習Alamofire框架,上一篇博客:Alamofire學習(一)網絡基礎 講解了一些網絡協議相關的知識,本篇博客主要是講解一下Alamofire框架用到的IOS系統API URLSession.
  • 爲何要先學習URLSession

Alamofire中是使用URLSession進行封裝的,因此有必要去先深刻了解下URLSession. URLSession等同於NSURLSession,只是前者是Swift中的名字,後者是OC中的名字,他們之間是能夠直接相互轉換的。NSURLSession是同iOS7一同推出的,主要是對NSURLConnection進行了重構和優化,而且NSURLConnection在iOS 9的時候也已經被廢棄,因此NSURLSession是NSURLConnection的取代者。安全

URLSession簡介

Alamofire是一個爲iOS和macOS打造的並基於Swift的網絡庫.它在Apple的基礎網絡架構上提供了更加優雅的接口來簡化繁重而經常使用的網絡請求任務。 Alamofire提供了鏈式的request/response方法,JSON的傳參和響應序列化,身份認證和其餘特性。Alamofire的優雅之處在於它完徹底全是由Swift寫成的,而且沒有從它的Objective-C版本-AFNetworking那繼承任何特性。服務器

由於咱們的Alamofire是對蘋果URLSession的封裝的網絡框架,因此在探索Alamofire以前,咱們除了須要掌握一些必備的網絡相關知識外,還須要熟悉IOS的系統API URLSessionmarkdown

先來看一張概要圖:cookie

URLSession-kyl

URLSession類成員屬性

1. URLSessionConfiguration

  • 3種會話模式
會話模式 特色 描述
默認會話模式(default) 工做模式相似於原來的NSURLConnection,使用的是基於磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證受權。 默認模式,一般咱們用這種模式就足夠了。default模式下系統會建立一個持久化的緩存並在用戶的鑰匙串中存儲證書
瞬時會話模式(ephemeral) 該模式不使用磁盤保存任何數據。全部和會話相關的caches,證書,cookies等都被保存在RAM中,所以當程序使會話無效,這些緩存的數據就會被自動清空。 系統沒有任何持久性存儲,全部內容的生命週期都與session相同,當session無效時,全部內容自動釋放。
後臺會話模式(background) 該模式在後臺完成上傳和下載,在建立Configuration對象的時候須要提供一個NSString類型的ID用於標識完成工做的後臺會話。 background建立一個能夠在後臺甚至APP已經關閉的時候仍然在傳輸數據的會話。background模式與default模式很是類似,不過background模式會用一個獨立線程來進行數據傳輸。background模式能夠在程序掛起,退出,崩潰的狀況下運行task。也能夠利用標識符來恢復進。注意,後臺Session必定要在建立的時候賦予一個惟一的identifier,這樣在APP下次運行的時候,可以根據identifier來進行相關的區分。若是用戶關閉了APP,IOS 系統會關閉全部的background Session。並且,被用戶強制關閉了之後,IOS系統不會主動喚醒APP,只有用戶下次啓動了APP,數據傳輸纔會繼續

2. URLSessionTask

2. 1 URLSessionDataTask:數據

URLSessionDataTask: 處理從HTTP get請求中從服務器獲取數據到內存中。網絡

Use URLSession’s dataTask(with:) and related methods to create URLSessionDataTask instances. Data tasks request a resource, returning the server’s response as one or more NSData objects in memory. They are supported in default, ephemeral, and shared sessions, but are not supported in background sessionssession

2. 2 URLSessionUploadTask:上傳

URLSessionUploadTask:上傳硬盤中的文件到服務器,通常是HTTP POST 或 PUT方式

Use URLSession’s uploadTask(with:from:) and related methods to create URLSessionUploadTask instances. Upload tasks are like data tasks, except that they make it easier to provide a request body so you can upload data before retrieving the server’s response. Additionally, upload tasks are supported in background sessions.

2. 3 URLSessionDownloadTask:下載

URLSessionDownloadTask: 從遠程服務器下載文件到臨時文件位置

Use URLSession’s downloadTask(with:) and related methods to create URLSessionDownloadTask instances. Download tasks download a resource directly to a file on disk. Download tasks are supported in any type of session.

2. 4 URLSessionStreamTask:流

Use URLSession’s streamTask(withHostName:port:) or streamTask(with:) to create URLSessionStreamTask instances. Stream tasks establish a TCP/IP connection from a host name and port or a net service object.

3. URLSessionDelegate

4. 緩存策略 NSURLRequestCachePolicy

緩存策略 做用 說明
NSURLRequestUseProtocolCachePolicy = 0 默認緩存策略 若是一個NSCachedURLResponse對於請求並不存在,數據將會從源端獲取。若是請求擁有一個緩存的響應,那麼URL加載系統會檢查這個響應來決定,若是它指定內容必須從新生效的話。假如內容必須從新生效,將創建一個連向源端的鏈接來查看內容是否發生變化。假如內容沒有變化,那麼響應就從本地緩存返回數據。若是內容變化了,那麼數據將從源端獲取
NSURLRequestReloadIgnoringLocalCacheData = 1 URL應該加載源端數據,不使用本地緩存數據
NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4 本地緩存數據、代理和其餘中介都要忽視他們的緩存,直接加載源數據
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData
NSURLRequestReturnCacheDataElseLoad = 2 指定已存的緩存數據應該用來響應請求,無論它的生命時長和過時時間。若是在緩存中沒有已存數據來響應請求的話,數據從源端加載
NSURLRequestReturnCacheDataDontLoad = 3 指定已存的緩存數據用來知足請求,無論生命時長和過時時間。若是在緩存中沒有已存數據來響應URL加載請求的話,不去嘗試從源段加載數據,此時認爲加載請求失敗。這個常量指定了一個相似於離線模式的行爲
NSURLRequestReloadRevalidatingCacheData = 5 指定若是已存的緩存數據被提供它的源段確認爲有效則容許使用緩存數據響應請求,不然從源段加載數據。

5. URLSession 屬性

屬性類別 屬性名稱 做用
常規
常規 identifier 配置對象的後臺會話標識符
常規 httpAdditionalHeaders 與請求一塊兒發送的附加頭文件的字典
常規 networkServiceType 網絡服務的類型
常規 allowsCellularAccess 一個布爾值,用於肯定是否應經過蜂窩網絡進行鏈接
常規 timeoutIntervalForRequest 等待其餘數據時使用的超時間隔
常規 timeoutIntervalForResource 資源請求應該容許的最大時間量
常規 sharedContainerIdentifier 應該下載後臺URL會話中的文件的共享容器的標識符
常規 waitsForConnectivity 一個布爾值,指示會話是否應等待鏈接變爲可用或者當即失敗
設置Cookie政策
httpCookieAcceptPolicy 決定什麼時候應該接受Cookie的策略常量
httpShouldSetCookies 一個布爾值,用於肯定請求是否應包含來自Cookie存儲的Cookie
httpCookieStorage 管理cookie存儲的單一對象(共享實例)
HTTPCookie 表示HTTP cookie的對象。它是一個不可變的對象,從包含cookie屬性的字典中初始化
設置安全策略
tlsMaximumSupportedProtocol 在此會話中進行鏈接時客戶端應請求的最大TLS協議版本
tlsMinimumSupportedProtocol 協議協商期間應該接受的最小TLS協議
urlCredentialStorage 提供身份驗證憑據的憑證存儲
設置緩存策略
urlCache 用於向會話中的請求提供緩存響應的URL緩存
requestCachePolicy 一個預約義常量,用於肯定什麼時候從緩存中返回響應
支持後臺轉移
sessionSendsLaunchEvents 一個布爾值,指示在傳輸完成時是否應該在後臺繼續或啓動應用程序
isDiscretionary 一個布爾值,用於肯定是否能夠根據系統的判斷來調度後臺任務以得到最佳性能
支持自定義協議
protocolClasses 在會話中處理請求的額外協議子類的數組
URLProtocol 一個NSURLProtocol對象處理加載協議特定的URL數據。在NSURLProtocol類自己是一個抽象類,能夠爲與特定URL方案的URL處理基礎設施。您能夠爲您的應用支持的任何自定義協議或URL方案建立子類
支持多路徑TCP
multipathServiceType 指定用於經過Wi-Fi和蜂窩接口傳輸數據的多路徑TCP鏈接策略的服務類型
URLSessionConfiguration.MultipathServiceType 指定多路徑TCP使用的服務類型的常量
設置HTTP策略和代理屬性
httpMaximumConnectionsPerHost 同時鏈接到給定主機的最大數量
httpShouldUsePipelining 一個布爾值,用於肯定會話是否應使用HTTP流水線
connectionProxyDictionary 包含有關在此會話中使用的代理信息的字典
支持鏈接變化
waitsForConnectivity 一個布爾值,指示會話是否應等待鏈接變爲可用或者當即失敗
屬性類別 屬性名稱 做用

URLSession使用

URLSession使用流程

URLSession使用流程

URLSession上傳

詳情能夠參考:蘋果官方文檔:Uploading Streams of Data

URLSession上傳

func postsesssionUploadTask(){
        //1.建立會話對象
        let config:URLSessionConfiguration=URLSessionConfiguration.default
        let session:URLSession=URLSession.init(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
        //2.根據會話對象建立task
         let urlstr="\(BASEURL)"+LUNBOURL
        let urls: NSURL = NSURL(string: urlstr)!
        //3.建立可變的請求對象
        var request:URLRequest  = URLRequest(url: urls as URL)
        //4.修改請求方法爲POST
        request.httpMethod = "POST"
        //5.設置請求體-----能夠不設置,有默認的
        request.httpBody = "".data(using: String.Encoding.utf8)
        //6.根據會話對象建立一個Task(發送請求)
        /* 第一個參數:請求對象 第二個參數:completionHandler回調(請求完成【成功|失敗】 data:響應體信息(指望的數據) response:響應頭信息,主要是對服務器端的描述 error:錯誤信息,若是請求失敗,則error有值 upDta:要上傳的二進制數據 */
let images:UIImage=UIImage.init(named: "bannerhomeOne")!
        let upData:Data=UIImagePNGRepresentation(images)!
//經過data數據上傳
        let upTask=session.uploadTask(with: request, from: upData) { (data, res, error) in
            //上傳完畢後
            if error != nil{
                print(error)
            }else{
                let str = String(data: data!, encoding: String.Encoding.utf8)
                print("上傳完畢:\(str)")
            }
        }
       upTask.resume()
}    

複製代碼

URLSession下載

普通下載

  • (1)先開啓一個下載task
let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())

        let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
        
        session.downloadTask(with: url).resume()
複製代碼
  • (2)設置代理,接受回調,保存文件
//下載完成以後就回調URLSessionDownloadDelegate代理
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("下載完成 - \(location)")
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        print(" bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)")
        print("下載進度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
    }
複製代碼

後臺下載

後臺下載除了須要普通下載的必須步驟(1,2)外還需作以下處理

  • (3)開啓後臺下載權限
//用於保存後臺下載的completionHandler
  var backgroundSessionCompletionHandler: (() -> Void)?
  
  func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
      self.backgroundSessionCompletionHandler = completionHandler
  }
複製代碼
  • (4)下載完成時,調用系統回調,更新屏幕
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
      print("後臺任務下載回來")
      DispatchQueue.main.async {
          guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
          backgroundHandle()
      }
  }
複製代碼

斷點續傳下載

  1. 定義好變量,並遵循NSURLSessionDownloadDelegate協議
var downloadTask : NSURLSessionDownloadTask?
    var partialData : NSData?
    var session : NSURLSession?
    var request : NSMutableURLRequest?
複製代碼
  1. 爲每一個按鈕增長點擊事件
//開始下載
    @IBAction func onDownLoad(sender: AnyObject) {
        self.downloadFile()
    }
    //掛起下載
    @IBAction func onSuspend(sender: AnyObject) {
        if(self.downloadTask != nil)
        {
            //掛起下載任務,將下載好的數據進行保存
            self.downloadTask?.cancelByProducingResumeData({ (resumeData:NSData!) -> Void in
                self.partialData = resumeData
                self.downloadTask = nil
            })
        }
// downloadTask!.suspend()
    }
    //恢復下載
    @IBAction func onResume(sender: AnyObject) {
        if(self.downloadTask == nil)
        {
            //判斷是否又已下載數據,有的話就斷點續傳,沒有就徹底從新下載
        if(self.partialData != nil)
        {
            self.downloadTask = self.session?.downloadTaskWithResumeData(self.partialData!)
            }
        else{
            self.downloadTask = self.session?.downloadTaskWithRequest(self.request!)
            }
        }
        downloadTask!.resume()
    }
    //開始下載文件
    func downloadFile()
    {
        NSLog("正在下載")
        //建立URL
        var urlStr:NSString = NSString(string: "http://cdn.wall88.com/51a317b5ef36713194.jpg")
        urlStr = urlStr.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        var url = NSURL(string: urlStr)!
        //建立請求
        request = NSMutableURLRequest(URL: url)
        //建立默認會話
        var sessionConfig : NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
        sessionConfig.timeoutIntervalForRequest = 20 //設置請求超時時間
        sessionConfig.allowsCellularAccess = true //是否容許蜂窩網絡下載
        //建立會話
        session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)//指定配置和代理
        downloadTask = session!.downloadTaskWithRequest(request!)
        downloadTask!.resume()
    }

複製代碼
  1. 根據下載內容的大小更新進度條
//設置頁面狀態
    func setUIStatus(totalBytesWritten : Int64,expectedToWrite totalBytesExpectedToWrite:Int64 )
    {
        //調用主線程刷新UI
        dispatch_async(dispatch_get_main_queue(), {
            if(Int(totalBytesExpectedToWrite) != 0 && Int(totalBytesWritten) != 0)
            {
                //更新進度條
                self.ps.progress = Float(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
                if(totalBytesExpectedToWrite == totalBytesWritten)
                {
                    self.lbl_hint.text! = "下載完畢"
                    UIApplication.sharedApplication().networkActivityIndicatorVisible = false
                    self.btn_download.enabled = true
                }
                else{
                    self.lbl_hint.text = "正在下載"
                    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
                }
            }
            }
        )
    }
複製代碼
  1. 下載文件
//任務完成,不論是否下載成功
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        self.setUIStatus(0, expectedToWrite: 0)
        if(error != nil)
        {
            NSLog("error is:\(error!.localizedDescription)")
        }
    }
    
    //下載完成
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        var error:NSError?
        var cachePath : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).first as NSString
        var savePath = cachePath.stringByAppendingPathComponent(lbl_title.text!)
        NSLog("\(savePath)")
        var saveUrl : NSURL = NSURL(fileURLWithPath: savePath)!
        var defalutManager = NSFileManager.defaultManager()
        //判斷文件是否存在,存在則刪除
        if(defalutManager.fileExistsAtPath(savePath))
        {
            defalutManager.removeItemAtPath(savePath, error: &error)
        }
        //下載成功後,文件是保存在一個臨時的目錄中的,須要本身拷置到該文件的目錄
        defalutManager.copyItemAtURL(location, toURL: saveUrl, error: &error)
        if(error != nil)
        {
        NSLog("\(error)")
        }
    }
    
    //下載中
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        self.setUIStatus(totalBytesWritten, expectedToWrite: totalBytesExpectedToWrite)
    }
複製代碼

參考大神博客:juejin.cn/post/684490…

相關文章
相關標籤/搜索