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爲前綴
[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];
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(回話)方式
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];
}
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------