iOS 中的NSURLSession

5.NSURLSession的基本使用

  • 5.1 涉及知識點

(1)使用步驟

    使用NSURLSession創建task,然后執行task

(2)關于task

    a.NSURLSessionTask是一個抽象類,本身不能使用,只能使用它的子類
    b.NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask

(3)發送get請求

    //1.創建NSURLSession對象(可以獲取單例對象)
    NSURLSession *session = [NSURLSession sharedSession];

    //2.根據NSURLSession對象創建一個Task

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    //方法參數說明
    /*
    注意:該block是在子線程中調用的,如果拿到數據之后要做一些UI刷新操作,那么需要回到主線程刷新
    第一個參數:需要發送的請求對象
    block:當請求結束拿到服務器響應的數據時調用block
    block-NSData:該請求的響應體
    block-NSURLResponse:存放本次請求的響應信息,響應頭,真實類型為NSHTTPURLResponse
    block-NSErroe:請求錯誤信息
     */
   NSURLSessionDataTask * dataTask =  [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {

        //拿到響應頭信息
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

        //4.解析拿到的響應數據
        NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
    }];

    //3.執行Task
    //注意:剛創建出來的task默認是掛起狀態的,需要調用該方法來啟動任務(執行任務)
    [dataTask resume];

(4)發送get請求的第二種方式

  //注意:該方法內部默認會把URL對象包裝成一個NSURLRequest對象(默認是GET請求)
    //方法參數說明
    /*
    //第一個參數:發送請求的URL地址
    //block:當請求結束拿到服務器響應的數據時調用block
    //block-NSData:該請求的響應體
    //block-NSURLResponse:存放本次請求的響應信息,響應頭,真實類型為NSHTTPURLResponse
    //block-NSErroe:請求錯誤信息
     */
- (nullable NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error))completionHandler;

(5)發送POST請求

        //1.創建NSURLSession對象(可以獲取單例對象)
    NSURLSession *session = [NSURLSession sharedSession];

    //2.根據NSURLSession對象創建一個Task

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];

    //創建一個請求對象,并這是請求方法為POST,把參數放在請求體中傳遞
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
        //拿到響應頭信息
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

        //解析拿到的響應數據
        NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
    }];

    //3.執行Task
    //注意:剛創建出來的task默認是掛起狀態的,需要調用該方法來啟動任務(執行任務)
    [dataTask resume];

6.NSURLSession下載文件-代理

  • 6.1 涉及知識點

(1)創建NSURLSession對象,設置代理(默認配置)

 //1.創建NSURLSession,并設置代理
    /*
     第一個參數:session對象的全局配置設置,一般使用默認配置就可以
     第二個參數:誰成為session對象的代理
     第三個參數:代理方法在哪個隊列中執行(在哪個線程中調用),如果是主隊列那么在主線程中執行,如果是非主隊列,那么在子線程中執行
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

(2)根據Session對象創建一個NSURLSessionDataTask任務(post和get選擇)

//創建task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];

//注意:如果要發送POST請求,那么請使用dataTaskWithRequest,設置一些請求頭信息
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url];

(3)執行任務(其它方法,如暫停、取消等)

    //啟動task
    //[dataTask resume];
    //其它方法,如取消任務,暫停任務等
    //[dataTask cancel];
    //[dataTask suspend];

(4)遵守代理協議,實現代理方法(3個相關的代理方法)

/*
 1.當接收到服務器響應的時候調用
     session:發送請求的session對象
     dataTask:根據NSURLSession創建的task任務
     response:服務器響應信息(響應頭)
     completionHandler:通過該block回調,告訴服務器端是否接收返回的數據
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler

/*
 2.當接收到服務器返回的數據時調用
 該方法可能會被調用多次
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data

/*
 3.當請求完成之后調用該方法
 不論是請求成功還是請求失敗都調用該方法,如果請求失敗,那么error對象有值,否則那么error對象為空
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

(5)當接收到服務器響應的時候,告訴服務器接收數據(調用block)

//默認情況下,當接收到服務器響應之后,服務器認為客戶端不需要接收數據,所以后面的代理方法不會調用
    //如果需要繼續接收服務器返回的數據,那么需要調用block,并傳入對應的策略

    /*
        NSURLSessionResponseCancel = 0, 取消任務
        NSURLSessionResponseAllow = 1,  接收任務
        NSURLSessionResponseBecomeDownload = 2, 轉變成下載
        NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 轉變成流
    */

    completionHandler(NSURLSessionResponseAllow);

7.NSURLSessionDownloadTask實現大文件下載

  • 7.1 涉及知識點

(1)使用NSURLSession和NSURLSessionDownload可以很方便的實現文件下載操作

 /*
     第一個參數:要下載文件的url路徑
     第二個參數:當接收完服務器返回的數據之后調用該block
     location:下載的文件的保存地址(默認是存儲在沙盒中tmp文件夾下面,隨時會被刪除)
     response:服務器響應信息,響應頭
     error:該請求的錯誤信息
     */
    //說明:downloadTaskWithURL方法已經實現了在下載文件數據的過程中邊下載文件數據,邊寫入到沙盒文件的操作
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * __nullable location, NSURLResponse * __nullable response, NSError * __nullable error)

(2)downloadTaskWithURL內部默認已經實現了變下載邊寫入操作,所以不用開發人員擔心內存問題

(3)文件下載后默認保存在tmp文件目錄,需要開發人員手動的剪切到合適的沙盒目錄

(4)缺點:沒有辦法監控下載進度


8.使用NSURLSessionDownloadTask實現大文件下載-監聽下載進度

  • 8.1 涉及知識點

(1)創建NSURLSession并設置代理,通過NSURLSessionDownloadTask并以代理的方式來完成大文件的下載

     //1.創建NSULRSession,設置代理
    self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    //2.創建task
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    self.downloadTask = [self.session downloadTaskWithURL:url];

    //3.執行task
    [self.downloadTask resume];

(2)常用代理方法的說明

    /*
 1.當接收到下載數據的時候調用,可以在該方法中監聽文件下載的進度
 該方法會被調用多次
 totalBytesWritten:已經寫入到文件中的數據大小
 totalBytesExpectedToWrite:目前文件的總大小
 bytesWritten:本次下載的文件數據大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
/*
 2.恢復下載的時候調用該方法
 fileOffset:恢復之后,要從文件的什么地方開發下載
 expectedTotalBytes:該文件數據的總大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/*
 3.下載完成之后調用該方法
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
/*
 4.請求完成之后調用
 如果請求失敗,那么error有值
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

(3)實現斷點下載相關代碼

    //如果任務,取消了那么以后就不能恢復了
    //    [self.downloadTask cancel];

    //如果采取這種方式來取消任務,那么該方法會通過resumeData保存當前文件的下載信息
    //只要有了這份信息,以后就可以通過這些信息來恢復下載
    [self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
        self.resumeData = resumeData;
    }];

    -----------
    //繼續下載
    //首先通過之前保存的resumeData信息,創建一個下載任務
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];

     [self.downloadTask resume];

(4)計算當前下載進度

    //獲取文件下載進度
    self.progress.progress = 1.0 * totalBytesWritten/totalBytesExpectedToWrite;

(5)局限性

01 如果用戶點擊暫停之后退出程序,那么需要把恢復下載的數據寫一份到沙盒,代碼復雜度更
02 如果用戶在下載中途未保存恢復下載數據即退出程序,則不具備可操作性

9.使用NSURLSessionDataTask實現大文件離線斷點下載(完整)

  • 9.1 涉及知識點

(1)關于NSOutputStream的使用

    //1. 創建一個輸入流,數據追加到文件的屁股上
    //把數據寫入到指定的文件地址,如果當前文件不存在,則會自動創建
    NSOutputStream *stream = [[NSOutputStream alloc]initWithURL:[NSURL fileURLWithPath:[self fullPath]] append:YES];

    //2. 打開流
    [stream open];

    //3. 寫入流數據
    [stream write:data.bytes maxLength:data.length];

    //4.當不需要的時候應該關閉流
    [stream close];

(2)關于網絡請求請求頭的設置(可以設置請求下載文件的某一部分)

    //1. 設置請求對象
    //1.1 創建請求路徑
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

    //1.2 創建可變請求對象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    //1.3 拿到當前文件的殘留數據大小
    self.currentContentLength = [self FileSize];

    //1.4 告訴服務器從哪個地方開始下載文件數據
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
    NSLog(@"%@",range);

    //1.5 設置請求頭
    [request setValue:range forHTTPHeaderField:@"Range"];

(3)NSURLSession對象的釋放

-(void)dealloc
{
    //在最后的時候應該把session釋放,以免造成內存泄露
    //    NSURLSession設置過代理后,需要在最后(比如控制器銷毀的時候)調用session的invalidateAndCancel或者resetWithCompletionHandler,才不會有內存泄露
    //    [self.session invalidateAndCancel];
    [self.session resetWithCompletionHandler:^{

        NSLog(@"釋放---");
    }];
}

(4)優化部分

    01 關于文件下載進度的實時更新
    02 方法的獨立與抽取

10.NSURLSession實現文件上傳

  • 10.1 涉及知識點

(1)實現文件上傳的方法

      /*
     第一個參數:請求對象
     第二個參數:請求體(要上傳的文件數據)
     block回調:
     NSData:響應體
     NSURLResponse:響應頭
     NSError:請求的錯誤信息
     */
    NSURLSessionUploadTask *uploadTask =  [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error)

(2)設置代理,在代理方法中監聽文件上傳進度

/*
 調用該方法上傳文件數據
 如果文件數據很大,那么該方法會被調用多次
 參數說明:
     totalBytesSent:已經上傳的文件數據的大小
     totalBytesExpectedToSend:文件的總大小
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"%.2f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}

(3)關于NSURLSessionConfiguration相關

01 作用:可以統一配置NSURLSession,如請求超時等
02 創建的方式和使用
//創建配置的三種方式
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);

//統一配置NSURLSession
-(NSURLSession *)session
{
    if (_session == nil) {

        //創建NSURLSessionConfiguration
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

        //設置請求超時為10秒鐘
        config.timeoutIntervalForRequest = 10;

        //在蜂窩網絡情況下是否繼續請求(上傳或下載)
        config.allowsCellularAccess = NO;

        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}


11.AFN框架基本使用

  • 11.1 AFN內部結構
AFN結構體
    - NSURLConnection
        + AFURLConnectionOperation
        + AFHTTPRequestOperation
        + AFHTTPRequestOperationManager(封裝了常用的 HTTP 方法)
            * 屬性
                * baseURL :AFN建議開發者針對 AFHTTPRequestOperationManager 自定義個一個單例子類,設置 baseURL, 所有的網絡訪問,都只使用相對路徑即可
                * requestSerializer :請求數據格式/默認是二進制的 HTTP
                * responseSerializer :響應的數據格式/默認是 JSON 格式
                * operationQueue
                * reachabilityManager :網絡連接管理器
            * 方法
                * manager :方便創建管理器的類方法
                * HTTPRequestOperationWithRequest :在訪問服務器時,如果要告訴服務器一些附加信息,都需要在 Request 中設置
                * GET
                * POST

    - NSURLSession
        + AFURLSessionManager
        + AFHTTPSessionManager(封裝了常用的 HTTP 方法)
            * GET
            * POST
            * UIKit + AFNetworking 分類
            * NSProgress :利用KVO

    - 半自動的序列化&反序列化的功能
        + AFURLRequestSerialization :請求的數據格式/默認是二進制的
        + AFURLResponseSerialization :響應的數據格式/默認是JSON格式
    - 附加功能
        + 安全策略
            * HTTPS
            * AFSecurityPolicy
        + 網絡檢測
            * 對蘋果的網絡連接檢測做了一個封裝
            * AFNetworkReachabilityManager

建議:
可以學習下AFN對 UIKit 做了一些分類, 對自己能力提升是非常有幫助的
  • 11.2 AFN的基本使用

(1)發送GET請求的兩種方式(POST同)

-(void)get1
{
    //1.創建AFHTTPRequestOperationManager管理者
    //AFHTTPRequestOperationManager內部是基于NSURLConnection實現的
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    //2.發送請求
    /*
     http://120.25.226.186:32812/login?username=ee&pwd=ee&type=JSON
     第一個參數:NSString類型的請求路徑,AFN內部會自動將該路徑包裝為一個url并創建請求對象
     第二個參數:請求參數,以字典的方式傳遞,AFN內部會判斷當前是POST請求還是GET請求,以選擇直接拼接還是轉換為NSData放到請求體中傳遞
     第三個參數:請求成功之后回調Block
     第四個參數:請求失敗回調Block
     */

    NSDictionary *param = @{
                            @"username":@"520it",
                            @"pwd":@"520it"
                            };

    //注意:字符串中不能包含空格
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {

        NSLog(@"請求成功---%@",responseObject);

    } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
            NSLog(@"失敗---%@",error);
    }];
}

-(void)get2
{
    //1.創建AFHTTPSessionManager管理者
    //AFHTTPSessionManager內部是基于NSURLSession實現的
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.發送請求
    NSDictionary *param = @{
                            @"username":@"520it",
                            @"pwd":@"520it"
                            };

    //注意:responseObject:請求成功返回的響應結果(AFN內部已經把響應體轉換為OC對象,通常是字典或數組)
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            NSLog(@"請求成功---%@",[responseObject class]);

    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        NSLog(@"失敗---%@",error);
    }];
}

(2)使用AFN下載文件

-(void)download
{
    //1.創建一個管理者
    AFHTTPSessionManager *manage  = [AFHTTPSessionManager manager];

    //2.下載文件
    /*
     第一個參數:請求對象
     第二個參數:下載進度
     第三個參數:block回調,需要返回一個url地址,用來告訴AFN下載文件的目標地址
         targetPath:AFN內部下載文件存儲的地址,tmp文件夾下
         response:請求的響應頭
         返回值:文件應該剪切到什么地方
     第四個參數:block回調,當文件下載完成之后調用
        response:響應頭
        filePath:文件存儲在沙盒的地址 == 第三個參數中block的返回值
        error:錯誤信息
     */

    //2.1 創建請求對象
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];

    //2.2 創建下載進度,并監聽
    NSProgress *progress = nil;

    NSURLSessionDownloadTask *downloadTask = [manage downloadTaskWithRequest:request progress:&progress destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {

        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

        //拼接文件全路徑
        NSString *fullpath = [caches stringByAppendingPathComponent:response.suggestedFilename];
        NSURL *filePathUrl = [NSURL fileURLWithPath:fullpath];
        return filePathUrl;

    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error) {

        NSLog(@"文件下載完畢---%@",filePath);
    }];

    //2.3 使用KVO監聽下載進度
    [progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];

    //3.啟動任務
    [downloadTask resume];
}

//獲取并計算當前文件的下載進度
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(NSProgress *)progress change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%zd--%zd--%f",progress.completedUnitCount,progress.totalUnitCount,1.0 * progress.completedUnitCount/progress.totalUnitCount);
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容