最近在研究NSULRSession,順道總結(jié)了NSURLConnection與NSULRSession區(qū)別與聯(lián)系,僅供交流學(xué)習(xí),歡迎各位大神指正。
NSURLConnection
NSURLConnection指的是一組構(gòu)成Foundation框架中URL加載系統(tǒng)的相互關(guān)聯(lián)的組件:NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache。
創(chuàng)建connection
// 1.URL
NSURL* url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];
//2.請(qǐng)求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//設(shè)置請(qǐng)求頭
NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
//3.下載
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
這里用到了代理,遵守NSURLConnectionDataDelegate 協(xié)議.
下面是代理方法
// 請(qǐng)求失敗時(shí)調(diào)用(請(qǐng)求超時(shí)、網(wǎng)絡(luò)異常)
-(void)connection:(NSURLConnectio**)connection didFailWithError:(NSError *)error{
}
// 1.接收到服務(wù)器的響應(yīng)就會(huì)調(diào)用
-(void)connection:(NSURLConnection**)connection didReceiveResponse:(NSURLResponse *)response{
}
// 2.當(dāng)接收到服務(wù)器返回的實(shí)體數(shù)據(jù)時(shí)調(diào)用(具體內(nèi)容,這個(gè)方法可能會(huì)被調(diào)用多次)
-(void)connection:(NSURLConnection**)connection didReceiveData:(NSData *)data{
}
// 3.加載完畢后調(diào)用(服務(wù)器的數(shù)據(jù)已經(jīng)完全返回后)
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
}
通過didReceiveData這個(gè)代理方法每次傳回來一部分文件,最終我們把每次傳回來的數(shù)據(jù)拼接合并成一個(gè)我們需要的文件寫入沙盒,最終就獲取到了我們需要的數(shù)據(jù),需要注意的在我們獲取一部分data的時(shí)候就寫入沙盒中,然后釋放內(nèi)存中的data,而不是直接用來一個(gè)接受文件的NSMutableData,它一直都在內(nèi)存中,會(huì)隨著文件的下載一直變大。
寫入的時(shí)候這里要用到NSFilehandle這個(gè)類,這個(gè)類可以實(shí)現(xiàn)對(duì)文件的讀取、寫入、更新。在接受到響應(yīng)的時(shí)候就在沙盒中創(chuàng)建一個(gè)空的文件,然后每次接收到數(shù)據(jù)的時(shí)候就拼接到這個(gè)文件的最后面,通過- (unsigned long long)seekToEndOfFile 這個(gè)方法,這樣在下載過程中內(nèi)存的問題就解決了。

斷點(diǎn)下載
暫停/繼續(xù)下載是我們下載中過程中必不可少的的功能了,如果沒有暫停功能,用戶體驗(yàn)相比會(huì)很差,而且實(shí)際場景下如果突然網(wǎng)絡(luò)不好中斷了,沒有實(shí)現(xiàn)斷點(diǎn)下載的話我們只能重新下載了,用戶體驗(yàn)非常不好。
下面我們來了解斷點(diǎn)下載功能。
NSURLConnection 只提供了一個(gè)cancel方法,這并不是暫停,而是取消下載任務(wù)。如果要實(shí)現(xiàn)斷點(diǎn)下載必須要了解HTTP協(xié)議中請(qǐng)求頭的Range,通過設(shè)置請(qǐng)求頭的Range我們可以指定下載的位置、大小。
如果我們這樣設(shè)置bytes=500-,表示從500字節(jié)以后的所有字節(jié),只需要在didReceiveData中記錄已經(jīng)寫入沙盒中文件的大小,把這個(gè)大小設(shè)置到請(qǐng)求頭中,因?yàn)榈谝淮蜗螺d肯定是沒有執(zhí)行過didReceive方法,self.currentLength也就為0,也就是從頭開始下。
代碼如下
-(void)ButtonAction:(UIButton *)sender
{
sender.selected = !sender.selected;
if (sender.selected) {
// 1.URL
NSURL* url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];
//2.請(qǐng)求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//設(shè)置請(qǐng)求頭
NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
//3.下載
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
} else {
[self.connection cancel];
self.connection = nil;
}
}
ps:為了提高下載的效率,我們一般采用多線程下載。
NSURLSession
NSURLSession是iOS7之后新的網(wǎng)絡(luò)接口,NSURLSession也是一組相互依賴的類,而NSURLSession的不同之處在于,它將NSURLConnection替換為 NSURLSession和 NSURLSessionConfiguration,以及3個(gè) NSURLSessionTask
的子類: NSURLSessionDataTask , NSURLSessionUploadTask, 和NSURLSessionDownloadTask。另外,上面的NSURLConnection要自己去控制內(nèi)存寫入相應(yīng)的位置,而NSURLSession則不需要手動(dòng)寫入沙盒,更加方便了我們的使用。
三種任務(wù)類型:
1.NSURLSessionDataTask : 普通的GET\POST請(qǐng)求
2.NSURLSessionDownloadTask : 文件下載3.NSURLSessionUploadTask : 文件上傳(很少用,一般服務(wù)器不支持)
NSURLSession 使用
NSURLSession請(qǐng)求
// 1.得到session對(duì)象
NSURLSession* session = [NSURLSession sharedSession];
NSURL* url = [NSURL URLWithString:@""];
// 2.創(chuàng)建一個(gè)task,任務(wù)
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//data返回?cái)?shù)據(jù)
}];
// [session dataTaskWithRequest:<#(NSURLRequest *)#> completionHandler:<#^(NSData *data, NSURLResponse *response, NSError *error)completionHandler#>]
//3.開始任務(wù)
[dataTask resume];
NSURLSession 下載
使用NSURLSession下載相對(duì)于NSURLConnection就非常簡單了,不需要去手動(dòng)控制邊下載邊寫入沙盒的問題,蘋果都幫我們做好了。
代碼如下
NSURL* url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];
// 得到session對(duì)象
NSURLSession *session = [NSURLSession sharedSession];
//創(chuàng)建任務(wù)
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// location : 臨時(shí)文件的路徑(下載好的文件),也就是下載好的文件寫入沙盒的地址,打印一下發(fā)現(xiàn)下載好的文件被自動(dòng)寫入的temp文件夾下面了。
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// response.suggestedFilename : 建議使用的文件名,一般跟服務(wù)器端的文件名一致
NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
// 將臨時(shí)文件剪切或者復(fù)制Caches文件夾
NSFileManager *mgr = [NSFileManager defaultManager];
// AtPath : 剪切前的文件路徑
// ToPath : 剪切后的文件路徑
[mgr moveItemAtPath:location.path toPath:file error:nil];
}];
// 開始任務(wù)
[downloadTask resume];
sandbox:/Users/maying/Library/Developer/CoreSimulator/Devices/42CE6C49-4CC6-47A3-8992-B8CABE1A9678/data/Containers/Data/Application/1A6948E7-78AC-478D-9751-E25AC199B359

但是在下載完成之后會(huì)自動(dòng)刪除temp中的文件,所有我們需要做的只是在回調(diào)中把文件移動(dòng)(或者復(fù)制,反正之后會(huì)自動(dòng)刪除)到caches中,也就是上面將臨時(shí)文件剪切或者復(fù)制Caches文件夾的過程。
下載完結(jié)果如下:

ps:通過這種方式下載有個(gè)缺點(diǎn)就是無法監(jiān)聽下載進(jìn)度,要監(jiān)聽下載進(jìn)度,我們通常的作法是通過delegate,而且NSURLSession的創(chuàng)建方式也有所不同。首先遵守協(xié)議<NSURLSessionDownloadDelegate>
協(xié)議里面有三個(gè)方法。
創(chuàng)建任務(wù)如下
// 得到session對(duì)象
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration] ;
//默認(rèn)配置
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//創(chuàng)建任務(wù)
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url];
//開始任務(wù)
[downloadTask resume];
協(xié)議方法如下
#pragma mark -- NSURLSessionDownloadDelegate
//1.下載完畢會(huì)調(diào)用 (@param location,文件臨時(shí)地址)
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// response.suggestedFilename:建議使用的文件名,一般跟服務(wù)器端的文件名一致
NSString *filePath = [cache stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
//將臨時(shí)文件剪切或復(fù)制到Caches文件夾
NSFileManager *fileManager = [NSFileManager defaultManager];
// AtPath : 剪切前的文件路徑 ,ToPath : 剪切后的文件路徑
[fileManager moveItemAtPath:location.path toPath:filePath error:nil];
NSLog(@"下載完成");
}
//2.執(zhí)行下載任務(wù)時(shí)有數(shù)據(jù)寫入,在這里面監(jiān)聽下載進(jìn)度(totalBytesWritten/totalBytesExpectedToWrite
@param bytesWritten 這次寫入的大小
@param totalBytesWritten 已經(jīng)寫入的大小
@param totalBytesExpectedToWrite 文件總大小)
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
self.myProgress.progress = (double)totalBytesWritten/totalBytesExpectedToWrite;
self.progressDesLabel.text = [NSString stringWithFormat:@"下載進(jìn)度%f:",(double)totalBytesWritten/totalBytesExpectedToWrite];
}
//3.恢復(fù)下載后調(diào)用
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
}
NSURLSessionDownloadTask斷點(diǎn)下載
取消任務(wù)
__weak typeof(self) weakSelf = self;
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
// resumeData : 包含了繼續(xù)下載的開始位置\下載的url
weakSelf.resumeData = resumeData;
weakSelf.downloadTask = nil;
}];
ps:需要注意的是Block中循環(huán)引用的問題
取消操作調(diào)用一個(gè)Block回調(diào)后傳入一個(gè)resumeData,該參數(shù)包含了繼續(xù)下載文件的位置信息。也就是說,當(dāng)我們下載了200M的文件數(shù)據(jù),突然暫停了。下次當(dāng)我們進(jìn)來的時(shí)候繼續(xù)下載的是從第200M這個(gè)位置開始的,而不是從文件最開始的位置開始下載。因而為了保存這些信息,所以才定義了resumeData這個(gè)NSData類型的屬性,這個(gè)data包含了url和繼續(xù)下載的位置,也就是已經(jīng)下載數(shù)據(jù)的大小。
通過resumeData來創(chuàng)建任務(wù)的方法
-(NSURLSessionDownloadTask**)downloadTaskWithResumeData:(NSData*)resumeData;
因此,我們要做的就是在取消操作的回調(diào)中記錄好resumeData,然后在恢復(fù)下載的時(shí)候調(diào)用上面的方法創(chuàng)建任務(wù)就好了,相對(duì)NSURLconnection手動(dòng)寫入沙盒方便了不少。需要注意的是下載比較耗費(fèi)資源,我們可以采用多線程分條下載后組成我們需要的文件數(shù)據(jù)。
本文示例demo下載:
本文Demo