ios斷點續傳:NSURLSession和NSURLSessionDataTask實現,

來源:互聯網
上載者:User

ios斷點續傳:NSURLSession和NSURLSessionDataTask實現,

蘋果提供的NSURLSessionDownloadTask雖然能實現斷點續傳,但是有些情況是無法處理的,比如程式強制退出或沒有調用

cancelByProducingResumeData取消方法,這時就無法斷點續傳了。

 

使用NSURLSession和NSURLSessionDataTask實現斷點續傳的過程是:

1、配置NSMutableURLRequest對象的Range要求標頭欄位資訊

2、建立使用代理的NSURLSession對象

3、使用NSURLSession對象和NSMutableURLRequest對象建立NSURLSessionDataTask對象,啟動任務。

4、在NSURLSessionDataDelegate的didReceiveData方法中追加擷取下載資料到目標檔案。

 

下面是具體實現,封裝了一個續傳管理器。可以直接拷貝到你的工程裡,也可以參考我提供的DEMO:http://pan.baidu.com/s/1c0BHToW

 

////  MQLResumeManager.h////  Created by MQL on 15/10/21.//  Copyright © 2015年. All rights reserved.// #import <Foundation/Foundation.h> @interface MQLResumeManager : NSObject /** *  建立斷點續傳管理對象,啟動下載請求 * *  @param url          檔案資源地址 *  @param targetPath   檔案存放路徑 *  @param success      檔案下載成功的回調塊 *  @param failure      檔案下載失敗的回調塊 *  @param progress     檔案下載進度的回調塊 * *  @return 斷點續傳管理對象 * */+(MQLResumeManager*)resumeManagerWithURL:(NSURL*)url                                targetPath:(NSString*)targetPath                                success:(void (^)())success                                failure:(void (^)(NSError*error))failure                               progress:(void (^)(longlongtotalReceivedContentLength,longlong totalContentLength))progress; /** *  啟動斷點續傳下載請求 */-(void)start; /** *  取消斷點續傳下載請求 */-(void)cancel;  @end

 

 

 

  1 //  2   3 //  MQLResumeManager.m  4   5 //  6   7 //  Created by MQL on 15/10/21.  8   9 //  Copyright © 2015年. All rights reserved. 10  11 // 12  13   14  15 #import "MQLResumeManager.h" 16  17   18  19 typedef void (^completionBlock)(); 20  21 typedef void (^progressBlock)(); 22  23   24  25 @interface MQLResumeManager ()<NSURLSessionDelegate,NSURLSessionTaskDelegate> 26  27   28  29 @property (nonatomic,strong)NSURLSession *session;   //注意一個session只能有一個請求任務 30  31 @property(nonatomic,readwrite,retain)NSError *error;//請求出錯 32  33 @property(nonatomic,readwrite,copy)completionBlockcompletionBlock; 34  35 @property(nonatomic,readwrite,copy)progressBlock progressBlock; 36  37   38  39 @property (nonatomic,strong)NSURL *url;          //檔案資源地址 40  41 @property (nonatomic,strong)NSString *targetPath;//檔案存放路徑 42  43 @property longlong totalContentLength;            //檔案總大小 44  45 @property longlong totalReceivedContentLength;    //已下載大小 46  47   48  49 /** 50  51  *  設定成功、失敗回調block 52  53  * 54  55  *  @param success 成功回調block 56  57  *  @param failure 失敗回調block 58  59  */ 60  61 - (void)setCompletionBlockWithSuccess:(void (^)())success 62  63                               failure:(void (^)(NSError*error))failure; 64  65   66  67 /** 68  69  *  設定進度回調block 70  71  * 72  73  *  @param progress 74  75  */ 76  77 -(void)setProgressBlockWithProgress:(void (^)(longlongtotalReceivedContentLength,longlong totalContentLength))progress; 78  79   80  81 /** 82  83  *  擷取檔案大小 84  85  *  @param path 檔案路徑 86  87  *  @return 檔案大小 88  89  * 90  91  */ 92  93 - (long long)fileSizeForPath:(NSString *)path; 94  95   96  97 @end 98  99  100 101 @implementation MQLResumeManager102 103  104 105 /**106 107  *  設定成功、失敗回調block108 109  *110 111  *  @param success 成功回調block112 113  *  @param failure 失敗回調block114 115  */116 117 - (void)setCompletionBlockWithSuccess:(void (^)())success118 119                               failure:(void (^)(NSError*error))failure{120 121     122 123     __weak typeof(self) weakSelf =self;124 125     self.completionBlock = ^ {126 127         128 129         dispatch_async(dispatch_get_main_queue(), ^{130 131             132 133             if (weakSelf.error) {134 135                 if (failure) {136 137                     failure(weakSelf.error);138 139                 }140 141             } else {142 143                 if (success) {144 145                     success();146 147                 }148 149             }150 151             152 153         });154 155     };156 157 }158 159  160 161 /**162 163  *  設定進度回調block164 165  *166 167  *  @param progress168 169  */170 171 -(void)setProgressBlockWithProgress:(void (^)(longlongtotalReceivedContentLength,longlong totalContentLength))progress{172 173     174 175     __weak typeof(self) weakSelf =self;176 177     self.progressBlock = ^{178 179         180 181         dispatch_async(dispatch_get_main_queue(), ^{182 183             184 185             progress(weakSelf.totalReceivedContentLength, weakSelf.totalContentLength);186 187         });188 189     };190 191 }192 193  194 195 /**196 197  *  擷取檔案大小198 199  *  @param path 檔案路徑200 201  *  @return 檔案大小202 203  *204 205  */206 207 - (long long)fileSizeForPath:(NSString *)path {208 209     210 211     long long fileSize =0;212 213     NSFileManager *fileManager = [NSFileManagernew];// not thread safe214 215     if ([fileManager fileExistsAtPath:path]) {216 217         NSError *error = nil;218 219         NSDictionary *fileDict = [fileManagerattributesOfItemAtPath:path error:&error];220 221         if (!error && fileDict) {222 223             fileSize = [fileDict fileSize];224 225         }226 227     }228 229     return fileSize;230 231 }232 233  234 235 /**236 237  *  建立斷點續傳管理對象,啟動下載請求238 239  *240 241  *  @param url          檔案資源地址242 243  *  @param targetPath   檔案存放路徑244 245  *  @param success      檔案下載成功的回調塊246 247  *  @param failure      檔案下載失敗的回調塊248 249  *  @param progress     檔案下載進度的回調塊250 251  *252 253  *  @return 斷點續傳管理對象254 255  *256 257  */258 259 +(MQLResumeManager*)resumeManagerWithURL:(NSURL*)url260 261                               targetPath:(NSString*)targetPath262 263                                  success:(void (^)())success264 265                                  failure:(void (^)(NSError*error))failure266 267                                 progress:(void (^)(longlongtotalReceivedContentLength,longlong totalContentLength))progress{268 269     270 271     MQLResumeManager *manager = [[MQLResumeManageralloc]init];272 273     274 275     manager.url = url;276 277     manager.targetPath = targetPath;278 279     [managersetCompletionBlockWithSuccess:successfailure:failure];280 281     [manager setProgressBlockWithProgress:progress];282 283     284 285     manager.totalContentLength =0;286 287     manager.totalReceivedContentLength =0;288 289     290 291     return manager;292 293 }294 295  296 297 /**298 299  *  啟動斷點續傳下載請求300 301  */302 303 -(void)start{304 305     306 307     NSMutableURLRequest *request = [[NSMutableURLRequestalloc]initWithURL:self.url];308 309     310 311     longlong downloadedBytes =self.totalReceivedContentLength = [selffileSizeForPath:self.targetPath];312 313     if (downloadedBytes > 0) {314 315         316 317         NSString *requestRange = [NSStringstringWithFormat:@"bytes=%llu-", downloadedBytes];318 319         [request setValue:requestRangeforHTTPHeaderField:@"Range"];320 321     }else{322 323         324 325         int fileDescriptor =open([self.targetPathUTF8String],O_CREAT |O_EXCL |O_RDWR,0666);326 327         if (fileDescriptor > 0) {328 329             close(fileDescriptor);330 331         }332 333     }334 335     336 337     NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfigurationdefaultSessionConfiguration];338 339     NSOperationQueue *queue = [[NSOperationQueuealloc]init];340 341     self.session = [NSURLSessionsessionWithConfiguration:sessionConfigurationdelegate:selfdelegateQueue:queue];342 343     344 345     NSURLSessionDataTask *dataTask = [self.sessiondataTaskWithRequest:request];346 347     [dataTask resume];348 349 }350 351  352 353 /**354 355  *  取消斷點續傳下載請求356 357  */358 359 -(void)cancel{360 361     362 363     if (self.session) {364 365         366 367         [self.sessioninvalidateAndCancel];368 369         self.session =nil;370 371     }372 373 }374 375  376 377 #pragma mark -- NSURLSessionDelegate378 379 /* The last message a session delegate receives.  A session will only become380 381  * invalid because of a systemic error or when it has been382 383  * explicitly invalidated, in which case the error parameter will be nil.384 385  */386 387 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullableNSError *)error{388 389     390 391     NSLog(@"didBecomeInvalidWithError");392 393 }394 395  396 397 #pragma mark -- NSURLSessionTaskDelegate398 399 /* Sent as the last message related to a specific task.  Error may be400 401  * nil, which implies that no error occurred and this task is complete.402 403  */404 405 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask*)task406 407 didCompleteWithError:(nullable NSError *)error{408 409     410 411     NSLog(@"didCompleteWithError");412 413     414 415     if (error == nil &&self.error ==nil) {416 417         418 419         self.completionBlock();420 421         422 423     }else if (error !=nil){424 425         426 427         if (error.code != -999) {428 429             430 431             self.error = error;432 433             self.completionBlock();434 435         }436 437         438 439     }else if (self.error !=nil){440 441         442 443         self.completionBlock();444 445     }446 447     448 449     450 451 }452 453  454 455 #pragma mark -- NSURLSessionDataDelegate456 457 /* Sent when data is available for the delegate to consume.  It is458 459  * assumed that the delegate will retain and not copy the data.  As460 461  * the data may be discontiguous, you should use462 463  * [NSData enumerateByteRangesUsingBlock:] to access it.464 465  */466 467 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask468 469     didReceiveData:(NSData *)data{470 471     472 473     //根據status code的不同,做相應的處理474 475     NSHTTPURLResponse *response = (NSHTTPURLResponse*)dataTask.response;476 477     if (response.statusCode ==200) {478 479         480 481         self.totalContentLength = dataTask.countOfBytesExpectedToReceive;482 483         484 485     }else if (response.statusCode ==206){486 487         488 489         NSString *contentRange = [response.allHeaderFieldsvalueForKey:@"Content-Range"];490 491         if ([contentRange hasPrefix:@"bytes"]) {492 493             NSArray *bytes = [contentRangecomponentsSeparatedByCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@" -/"]];494 495             if ([bytes count] == 4) {496 497                 self.totalContentLength = [[bytesobjectAtIndex:3]longLongValue];498 499             }500 501         }502 503     }else if (response.statusCode ==416){504 505         506 507         NSString *contentRange = [response.allHeaderFieldsvalueForKey:@"Content-Range"];508 509         if ([contentRange hasPrefix:@"bytes"]) {510 511             NSArray *bytes = [contentRangecomponentsSeparatedByCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@" -/"]];512 513             if ([bytes count] == 3) {514 515                 516 517                 self.totalContentLength = [[bytesobjectAtIndex:2]longLongValue];518 519                 if (self.totalReceivedContentLength==self.totalContentLength) {520 521                     522 523                     //說明已下完524 525                     526 527                     //更新進度528 529                     self.progressBlock();530 531                 }else{532 533                     534 535                     //416 Requested Range Not Satisfiable536 537                     self.error = [[NSErroralloc]initWithDomain:[self.urlabsoluteString]code:416userInfo:response.allHeaderFields];538 539                 }540 541             }542 543         }544 545         return;546 547     }else{548 549         550 551         //其他情況還沒發現552 553         return;554 555     }556 557     558 559     //向檔案追加資料560 561     NSFileHandle *fileHandle = [NSFileHandlefileHandleForUpdatingAtPath:self.targetPath];562 563     [fileHandle seekToEndOfFile]; //將節點跳到檔案的末尾564 565     566 567     [fileHandle writeData:data];//追加寫入資料568 569     [fileHandle closeFile];570 571     572 573     //更新進度574 575     self.totalReceivedContentLength += data.length;576 577     self.progressBlock();578 579 }580 581  582 583  584 585 @end

 

 

 

 

 

經驗證,如果app後台能運行,datatask是支援後台傳輸的。
讓您的app成為後台運行app非常簡單:


#import "AppDelegate.h"
static UIBackgroundTaskIdentifier bgTask;


@interface AppDelegate ()


@end


@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    
    [self getBackgroundTask];
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    [self endBackgroundTask];
}


/**
 *  擷取背景工作
 */
-(void)getBackgroundTask{
    
    NSLog(@"getBackgroundTask");
    UIBackgroundTaskIdentifier tempTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        
    }];
    
    if (bgTask != UIBackgroundTaskInvalid) {
        
        [self endBackgroundTask];
    }
    
    bgTask = tempTask;
    
    [self performSelector:@selector(getBackgroundTask) withObject:nil afterDelay:120];
}


/**
 *  結束背景工作
 */
-(void)endBackgroundTask{
    
    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}


@end

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.