NSURLConnection、NSURLSession

NSURLConnection
 
一、準備網絡資源地址:URL
注意:因爲URL支持26個英文字母,數字和少數的幾個特殊字符. 所以對於URL中包含非標準URL的字符,須要進行編碼. iOS提供了函數 stringByAddPercentEscapesUsingEncoding對中文和一些特殊字符進行編碼
二、發送數據請求
三、遵照協議:<NSURLConnectionDataDelegate>,處理下載任務時的事件
 
@interface ViewController ()<NSURLConnectionDataDelegate>
//用於存儲網絡下載的數據.把分包的數據整合起來.
@property (nonatomic, strong) NSMutableData *receivedData;

@end
//!!請注意URL地址必須包含http等協議的前綴. xcode並不會自動爲咱們添加.
//    NSURL OC的URL類型,地址資源定位
    NSURL *url = [NSURL URLWithString:@" http://www.tmooc.cn"];
// OC的請求類型,包含了請求相關的操做.例如緩存.cookie等等...
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
//    Immediately 馬上,立刻
//    startImmediately 馬上開始
//    YES表明馬上開始請求,
//    NO 不須要馬上開始,須要手動觸發.
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
//    手動開始請求
    [conn start];
}
 
//一、當收到服務器的請求響應,觸發此代理
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"didReceiveResponse:%@", response);
    _receivedData = [[NSMutableData alloc] init];
}
//二、收到服務器傳輸的數據,若是數據量比較大的話,服務器會分批傳,俗稱數據包.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//    因爲data有多是分批的,因此咱們須要一個全局的MutableData去拼接起來
    NSLog(@"didReceiveData:%ld個字節", data.length);
    [_receivedData appendData:data];
}
//三、鏈接成功被加載了
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"connectionDidFinishLoading");
//    寫到沙盒的document文件夾下的baidu文件中
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/baidu"];
    NSLog(@"path is %@", path);
//    data類型轉換爲字符串,再存儲
//    使用NSUTF8StringEncoding編碼類型,對_receivedData這個二進制類型進行轉碼,轉換爲字符串
    NSString *baiduStr = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
//    定義一個error指針,不須要初始化
//    error指針的初始化由下面的方法來完成. 若是有錯誤就會被賦值. 沒錯誤,依然會是nil
    NSError *error = nil;
    [baiduStr writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&error];
    if (error) {
//        error有值,則說明上面的寫文件方法出錯了
        NSLog(@"error:%@", error);
    }
}
 
//——————————————————--------------------—數據請求的方式----------------------------------------
//一般咱們使用宏定義 或者 static的方式 在文件的頂端,聲明文件中須要用到的常量
//好處:便於維護. 特別是一個常量 要不少個位置使用時
//習慣上,添加k爲前綴
#define kBaiduURLString    @" http://www.baidu.com"
 
    [selfsendSyncRequest:kBaiduURLString];
 
異步請求:(在子線程執行)
- (void)sendAsyncRequest:(NSString *)urlString{
//轉碼: 對urlString進行標準化的編碼,防止中文和特殊字符的出現
//    Percent 百分號,分數
    urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSLog(@"主線程是%@", [NSThread mainThread]);
//異步請求方法:
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
//下載結束後的處理:
        NSLog(@"在線程%@中處理",[NSThread currentThread]);
        NSLog(@"接收到%ld字節的數據", data.length);
    }];
}
 
同步請求:(不用,在主線程執行)
    urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
//    用於存儲服務器返回的響應,參考delegate方式中的didReceiveResponse
    NSURLResponse *response = nil;
    NSError *error = nil;
//    發送同步請求
    NSTimeInterval beginTime =[NSDate timeIntervalSinceReferenceDate];
    NSData *receivedData =[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSLog(@"接收到%ld個字節的數據", receivedData.length);
    NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate];
    NSLog(@"請求耗時%f秒", endTime - beginTime);
//——————————————————------------------------—End--------------------------------------------
 
//———————————————————-------------------斷點下載----------------------------------------------
服務器: 被動的角色
客戶端: 主動的角色
客戶端必須告訴服務器當前應該從哪一個地方開始繼續下載

如何告訴, 經過http協議的數據包?
客戶端 —http> 服務器
key(BigFile.zip: 600M):
1) 客戶端Request請求頭中Range: bytes=100-200 ;—> 服務器從100bytes開時發送給客戶端, 直到200bytes
2) 客戶端Request請求頭中Range: bytes=100- ;—> 服務器從100bytes開時發送給客戶端, 直到發送完畢爲止
步驟:
1. 使用NSFileManager在didReceiveResponse方法中建立一個空的文件(在沙盒中的caches路徑下),獲取文件的總大小
2. 在didReceiveData方法中: 首先將writeHandler移到文件的最後, 以後再將數據寫入文件中.
3. 在DidFinishLoading方法中, 對屬性從新初始化,在關閉文件寫句柄
 
//Caches路徑
@property(nonatomic,strong)NSString *cachePath;
@property(nonatomic,strong)NSFileHandle *writeHandle;
@property(nonatomic,assign)long long currentLength;//已下載的文件的大小(以字節爲單位)
@property(nonatomic,assign)long long totalLength;//文件的總大小
//斷點傳輸
@property(nonatomic,strong)NSMutableURLRequest *request;//要使用可變的請求對象
@property(nonatomic,strong)NSURLConnection *conn;

@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.cachePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject];
 
}
- (IBAction)downloadBigFile:(UIButton *)sender {
    NSURL *url=[NSURL URLWithString:@" http://localhost:80/Evernote.zip"];
 
    self.totalLength=0;
    self.currentLength=0;
    self.request=[NSMutableURLRequest requestWithURL:url];
    //設置range:bytes=0-
    NSString *range= [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
    [self.request setValue:range forHTTPHeaderField:@"Range"];
    self.conn = [NSURLConnection connectionWithRequest:self.request delegate:self];
}
- (IBAction)cancelDownLoad:(UIButton *)sender {
    //取消下載
    [self.conn cancel];
    self.conn=nil;
}
- (IBAction)resumeDownLoad:(UIButton *)sender {
    //恢復下載
    //Range:bytes=
    NSString *range=[NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
    [self.request setValue:range forHTTPHeaderField:@"Range"];
    self.conn = [NSURLConnection connectionWithRequest:self.request delegate:self];
}
//客戶端請求成功,接收到響應response
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
//獲取返回的代號,判斷出服務器響應的結果
    NSInteger status=[(NSHTTPURLResponse *)response statusCode];
    NSLog(@"status:%ld",(long)status);
    if (status == 206) {
        //在沙盒Caches/文件夾下,建立出用來存儲數據的文件
        NSFileManager *manage=[NSFileManager defaultManager];
        NSString *fliePath=[self.cachePath stringByAppendingPathComponent:@"text.zip"];
        NSLog(@"寫入的文件路徑:%@",fliePath);
        [manage createFileAtPath:fliePath contents:nil attributes:nil];
        //獲取文件的總大小(爲了顯示進度)
        self.writeHandle=[NSFileHandle fileHandleForWritingAtPath:fliePath];
        if (self.currentLength ==0) {//服務器返回文件的剩餘大小,所以要獲取一次返回的大小
            self.totalLength=response.expectedContentLength;
        } 
        NSLog(@"文件的總大小:%lld",self.totalLength); 
    }
}
//接收到數據
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    //當前接收數據包的大小
    static int i=0;
    i++;
    NSLog(@"當前接收數據大小_%d:%ld",i,(unsigned long)data.length);
    //移動到文件尾部
    [self.writeHandle seekToEndOfFile];
   //寫入文件
    [self.writeHandle writeData:data];
    self.currentLength +=data.length;
    NSLog(@"總接收數據:%lld",self.currentLength);
    self.progressView.progress = (double)self.currentLength / self.totalLength;
    NSLog(@"當前進度:%f",self.progressView.progress);
}
//下載完成後,關閉句柄
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    [self.writeHandle closeFile];
}
//錯誤處理
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    NSLog(@"error : %@",[error userInfo])
}
 
//*********************************************
蘋果公司在iOS7,給SDK中添加了新的網絡處理類:NSURLSession
它與URLConnection是並列關係.
URLSession包含了URLConnection的全部功能, 額外還添加了如下4點特性:
1.經過URL將數據下載到內存
2.經過URL將數據下載到文件系統
3.將數據上傳到指定的URL
4.在後臺完成上述的操做
適用場景:     ios7+; 封裝不少實現細節;
使用NSURLSession以前
選擇工做模式:
session(會話)三個類型:
session1  <- defaultSessionConfiguration:(默認模式),相似於原來的NSURLConnection,使用的是基於磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證受權
session2  <- ephemeralSessionConfiguration:(瞬時模式),不會在磁盤中保存任何數據,全部與會話有關的caches、證書、cookies等都保存在RAM中,所以當程序會話無效時,這些緩存數據就會被自動清理。
session3  <- backgroundSessionConfiguration:(後臺模式),在後天完成上傳和下載,在建立congfiguration對象的時候須要提供一個NSString類型的ID用來標示完成工做的後臺會話。

task三個類型:
一、NSURLSessionDataTask:
     處理小數據相關的任務(不包括下載和上傳)
     例子: 返回json數據; 返回html文件(放在UIWebView);
               返回xml數據
二、NSURLSessionDownloadTask:
//採用的是子線程下載
     主要用於下載任務; delegate方法接收或者處理服務器返回的數據
三、NSURLSessionUploadTask:
     主要用於上傳任務;
後臺運行:
主要configuration的配置: backgroundSessionConfiguration
得到NSURLSession對象:
第一種方式是使用靜態的sharedSession方法,該類使用共享的會話,該會話使用全局的Cache,Cookie和證書。
第二種方式是經過sessionWithConfiguration:方法建立對象,也就是建立對應配置的會話,與NSURLSessionConfiguration合做使用。
第三種方式是經過sessionWithConfiguration:delegate:delegateQueue方法建立對象,二三兩種方式能夠建立一個新會話並定製其會話類型。該方式中指定了session的委託和委託所處的隊列。當再也不須要鏈接時,能夠調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束後關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件以後會被解引用。
 
//---------------------------------使用步驟--------------------------------------------------------
一、準備網絡資源地址URL、發送請求NSURLRequest
二、建立NSURLSession對象
三、執行下載任務NSURLSessionDownloadTask
四、下載完成後,對文件的處理
NSURLSessionDownloadTask  *downloadTask=[session downloadTaskWithRequest:request
                    completionHandler:^(NSURL *location,//location是位於沙盒中的tmp目錄下,要轉移(會被定時清理的) 
                    NSURLResponse *response,//服務器的響應
                    NSError *error){
          });
//------------------------------------------------------------------------------------------------
//————————————————-----------—————簡單的使用方式-------------------------------------------
 
- (IBAction)downLoad:(UIButton *)sender {
    //1建立NSURLSession對象
    NSURLSession *session=[NSURLSession sharedSession];//使用默認的session(回話)方式
    NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@" https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=298400068,822827541&fm=116&gp=0.jpg"]];
    NSURLSessionDownloadTask  *downloadTask=[session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        NSLog(@"下載完成:%@",[NSThread currentThread]);//子線程
       
        NSInteger status=[(NSHTTPURLResponse *)response statusCode];//服務器返回的編號
        NSLog(@"status:%ld",(long)status);
        if (status ==200) {
            NSLog(@"location:%@",location);
//location是位於沙盒中的tmp目錄下,要轉移(會被定時清理的)
//要將文件轉移到Caches中
            //一、獲取路徑
            NSLog(@"response : %@",response);
            NSString *path=[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject]stringByAppendingPathComponent:@"mm.jpg"];
            NSLog(@"path :%@",path);
            //二、移動文件
            [[NSFileManager defaultManager]moveItemAtPath:location.path toPath:path error:nil];
            //顯示圖片
            UIImage *image=[UIImage imageNamed:path];
//4.回到主線程將圖片顯示在界面上,爲何要回到主線程?答:由於視圖層都在主線程中
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image=image;
            });
        }
    }];
    //執行下載任務
    [downloadTask resume];
}
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
相關文章
相關標籤/搜索