NSURLSession介紹以及斷點下載(完整)

注意點:

#1,文件的保存路徑不能是網(wǎng)址!?。?#2,NSOutputStream的作用就是保存網(wǎng)路下載的數(shù)據(jù)。

1,NSHTTPURLResponse響應(yīng)對象

1,statusCode:狀態(tài)碼,可以根據(jù)這個值判斷是否請求出錯。
2,allHeaderFields:獲得響應(yīng)體內(nèi)容
3,URL:一般使用在重定向,如果不需要重定向,響應(yīng)的url和請求的url是一樣的。
4,MIMEType:服務(wù)器告訴客戶端返回的數(shù)據(jù)類型
5,textEncodingName :服務(wù)器告訴客戶端返回內(nèi)容的編碼格式
6,expectedContentLength:服務(wù)器返回數(shù)據(jù)的長度,客戶端可以通過該屬性獲得文件大小
7,suggestedFilename:服務(wù)器建議客戶端保存文件使用的名字

2,參考:iOS中流(NSStream)的使用

#NSInputStream 和 NSOutputStream
1,NSInputStream 和 NSOutputStream 是NSStream的兩個子類,分別對應(yīng)了讀文件和 寫文件。
2,NSInputStream 和 NSOutputStream其實是對 CoreFoundation 層對應(yīng)的CFReadStreamRef 和 CFWriteStreamRef 的高層抽象。

3,斷點續(xù)傳

#1,NSURLSessionTask是一個抽象類,本身不能使用,只能使用它的子類
NSURLSessionDataTask((完美斷點)下載)、
NSURLSessionUploadTask(上傳)、
NSURLSessionDownloadTask((有缺陷斷點)下載)

注意:請求的時候,只要有completionHandler結(jié)果回調(diào)的,那么都不會走代理方法。只有下載完成之后才會走completionHandler回調(diào)。

#2,NSURLSessionDownloadTask實現(xiàn)斷點下載(有缺陷)
//如果任務(wù),取消了那么以后就不能恢復(fù)了
     //    [self.downloadTask cancel];
     
     //如果采取這種方式來取消任務(wù),那么該方法會通過resumeData保存當前文件的下載信息
     //只要有了這份信息,以后就可以通過這些信息來恢復(fù)下載
     [self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
     self.resumeData = resumeData;
     }];
     
     -----------
     //繼續(xù)下載
     //首先通過之前保存的resumeData信息,創(chuàng)建一個下載任務(wù)
     self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
     
     [self.downloadTask resume];
     
     局限性:
     
     01 如果用戶點擊暫停之后退出程序,那么需要把恢復(fù)下載的數(shù)據(jù)寫一份到沙盒,代碼復(fù)雜度增加
     02 如果用戶在下載中途未保存恢復(fù)下載數(shù)據(jù)即退出程序,則不具備可操作性
#3,NSURLSessionDownloadTask兩種請求方法介紹
//    1,該方法不會默認保存存到沙盒tmp文件中(可以通過NSURLSessionDownloadTask并以代理的方式來完成大文件的下載,但是需要手動存到沙盒中)
        NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithURL:url];

//    2,內(nèi)部默認已經(jīng)實現(xiàn)了邊下載邊寫入沙盒tmp文件中操作,所以不用開發(fā)人員擔(dān)心內(nèi)存問題
    //注意,tmp中存儲的后綴是.tmp,可以通過剪切文件來修改后綴。
    //    缺點:不能監(jiān)聽下載的進度。
//    NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];

4,任務(wù)的三種操作

    #啟動task:
    [dataTask resume];
    #取消task
    [dataTask cancel];
    #暫停task
    [dataTask suspend];

5,實現(xiàn)斷點下載最好的方式是使用NSURLSessionDataTask實現(xiàn)大文件離線斷點下載

#流程:
文件是否下載完成-->文件下載中還是暫停下載-->是否存在文件下載的目錄-->創(chuàng)建流,用NSURLSessionDataTask實現(xiàn)斷點下載(如下)
#注意:一個下載文件URL需要兩部分:task和stream
#HSFileName(url):表示通過URL加密后對應(yīng)的文件名
#通過taskIdentifier獲取task的時候,需要強轉(zhuǎn)一下
//根據(jù)url獲得對應(yīng)的下載任務(wù)
- (NSURLSessionDataTask *)getTask:(NSString *)url
{
    return (NSURLSessionDataTask *)[self.tasks valueForKey:HSFileName(url)];
}
#正文:
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    
    // 創(chuàng)建流
    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:HSFileFullpath(url) append:YES];
    
    // 創(chuàng)建請求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    
    // 設(shè)置請求頭(從上次下載的長度開始繼續(xù)下載)
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-", HSDownloadLength(url)];
    [request setValue:range forHTTPHeaderField:@"Range"];
    
    // 創(chuàng)建一個Data任務(wù)(若之前已經(jīng)下載過一部分的文件了,那么這里是從上次下載到的位置開始下載的,這里創(chuàng)建的task就會賦值上新的taskIdentifier,并且把原來URL對應(yīng)的task替換掉。)
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    NSUInteger taskIdentifier = arc4random() % ((arc4random() % 10000 + arc4random() % 10000));
    [task setValue:@(taskIdentifier) forKeyPath:@"taskIdentifier"];

    // 保存任務(wù)
    [self.tasks setValue:task forKey:HSFileName(url)];

    HSSessionModel *sessionModel = [[HSSessionModel alloc] init];
    sessionModel.url = url;
    sessionModel.progressBlock = progressBlock;
    sessionModel.stateBlock = stateBlock;
    sessionModel.stream = stream;
    [self.sessionModels setValue:sessionModel forKey:@(task.taskIdentifier).stringValue];
    //開始下載(然后就到代理方法中進行操作即可)
    [self start:url];

#pragma mark - 代理
#pragma mark NSURLSessionDataDelegate
/**
 * 接收到響應(yīng)(進行打開下載任務(wù)對應(yīng)的流,然后保存該文件的總大小到本地。)
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    
    HSSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier];
    
    // 打開流
    [sessionModel.stream open];
    
    // 獲得服務(wù)器這次請求 返回數(shù)據(jù)的總長度(Content-Length對應(yīng)的是當前下載的URL對應(yīng)的文件大小,也許文件已經(jīng)下載過一部了,現(xiàn)在是繼續(xù)下載的,所以這里后面又加上了已經(jīng)下載的文件的大?。?    NSInteger totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + HSDownloadLength(sessionModel.url);
    sessionModel.totalLength = totalLength;
    
    //獲取plist文件 (存儲總長度到plist文件中,HSTotalLengthFullpath:plist文件的路徑)
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:HSTotalLengthFullpath];
    if (dict == nil) dict = [NSMutableDictionary dictionary];
    dict[HSFileName(sessionModel.url)] = @(totalLength);
    [dict writeToFile:HSTotalLengthFullpath atomically:YES];
    
    // 接收這個請求,允許接收服務(wù)器的數(shù)據(jù)。(因為系統(tǒng)默認是不響應(yīng)下載任務(wù)的)
    completionHandler(NSURLSessionResponseAllow);
}

/**
 * 接收到服務(wù)器返回的數(shù)據(jù)
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    HSSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier];
    
    // 寫入數(shù)據(jù)
    [sessionModel.stream write:data.bytes maxLength:data.length];
    
    // 下載進度
    NSUInteger receivedSize = HSDownloadLength(sessionModel.url); //已經(jīng)下載的文件的大小
    NSUInteger expectedSize = sessionModel.totalLength; //文件的總大小
    CGFloat progress = 1.0 * receivedSize / expectedSize;

    sessionModel.progressBlock(receivedSize, expectedSize, progress);
}

/**
 * 請求完畢(成功|失?。? */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    HSSessionModel *sessionModel = [self getSessionModel:task.taskIdentifier];
    if (!sessionModel) return;
    
    if ([self isCompletion:sessionModel.url]) {
        // 下載完成
        sessionModel.stateBlock(DownloadStateCompleted);
    } else if (error){
        // 下載失敗
        sessionModel.stateBlock(DownloadStateFailed);
    }
    
    // 關(guān)閉流
    [sessionModel.stream close];
    sessionModel.stream = nil;
    
    // 清除任務(wù)
    [self.tasks removeObjectForKey:HSFileName(sessionModel.url)];
    [self.sessionModels removeObjectForKey:@(task.taskIdentifier).stringValue];
}

1,本文參考鏈接
2,NSURLSession上傳文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • API定義規(guī)范 本規(guī)范設(shè)計基于如下使用場景: 請求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請求頻率非常高,建議使用雙通...
    有涯逐無涯閱讀 2,927評論 0 6
  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,741評論 6 152
  • iOS開發(fā)系列--網(wǎng)絡(luò)開發(fā) 概覽 大部分應(yīng)用程序都或多或少會牽扯到網(wǎng)絡(luò)開發(fā),例如說新浪微博、微信等,這些應(yīng)用本身可...
    lichengjin閱讀 4,040評論 2 7
  • 在學(xué)校從事英語教學(xué)十年有余,搭乘十個月二胎政策的春風(fēng)車之后,不得不與我心愛的工作依依惜別。或許是好為人師,或者是舊...
    笨媽媽與淘氣包閱讀 399評論 0 1

友情鏈接更多精彩內(nèi)容