實現重點:
- NSURLSessionDataTask要設置請求頭,從路徑中獲取文件已經下載的長度(文件沒有下載過的話,長度為0)。通過這個長度設置請求的Range
如圖:
- 接收到請求的時候key:文件名(經過MD5加密過的URL,Url保證了文件名的唯一) Value:該文件已經下載過的長度。保存成plist文件,方便對下載文件的判斷
- 利用NSOutUpStream寫文件
- 在任務完成的代理方法里面,NSOutUpStream關閉並且清空,對應的task清空,對應的session清空
代碼如下:
1 #import "ViewController.h" 2 #import "NSString+Hash.h" 3 4 // 下載文件的URL 5 #define ChaosFileURL @"http://120.25.226.186:32812/resources/videos/minion_01.mp4" 6 7 // 根據文件唯一的URL MD5值 作為文件名 8 #define ChaosFileName ChaosFileURL.md5String 9 10 // 用來存儲文件總長度的plist文件 key:文件名的MD5值 value:文件總長度 11 #define ChaosDownloadFilesPlist [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"downloadFiles.plist"] 12 13 // 下載文件的全路徑 14 #define ChaosFileFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:ChaosFileName] 15 16 // 已經下載的文件長度 17 #define ChaosDownloadLength [[[NSFileManager defaultManager] attributesOfItemAtPath:ChaosFileFullPath error:nil][@"NSFileSize"] integerValue] 18 19 @interface ViewController () <NSURLSessionDataDelegate> 20 21 /** stream */ 22 @property(nonatomic,strong) NSOutputStream *stream; 23 24 /** session */ 25 @property(nonatomic,strong) NSURLSession *session; 26 27 /** task */ 28 @property(nonatomic,strong) NSURLSessionDataTask *task; 29 30 /** totalLength */ 31 @property(nonatomic,assign) NSInteger totalLength; 32 33 /** downloadLength */ 34 @property(nonatomic,assign) NSInteger downloadLength; 35 36 @end 37 38 @implementation ViewController 39 40 - (NSURLSession *)session 41 { 42 if (_session == nil) { 43 44 _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; 45 } 46 47 return _session; 48 } 49 50 - (NSURLSessionDataTask *)task 51 { 52 if (_task == nil) { 53 54 // 獲得文件總長度 55 NSInteger totalLength = [[NSDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist][ChaosFileName] integerValue]; 56 // 請求同一個文件,判斷下載文件長度;如果沒下載過此文件,totalLength = 0 57 if (totalLength && ChaosDownloadLength == totalLength) { 58 NSLog(@"文件已經下載過."); 59 return nil; 60 } 61 62 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ChaosFileURL]]; 63 64 // 設置請求頭 -- range 這次從哪里開始請求數據 格式:bytes=***-***(從指定開始到指定結束) 或者:bytes=***-(從指定位置到結束) 65 NSString *range = [NSString stringWithFormat:@"bytes=%zd-",ChaosDownloadLength]; 66 67 [request setValue:range forHTTPHeaderField:@"Range"]; 68 69 _task = [self.session dataTaskWithRequest:request]; 70 71 } 72 return _task; 73 } 74 75 // 開始 76 - (IBAction)startClick:(id)sender { 77 78 [self.task resume]; 79 } 80 // 暫停 81 - (IBAction)pauseClick:(id)sender { 82 83 [self.task suspend]; 84 } 85 86 - (void)viewDidLoad { 87 [super viewDidLoad]; 88 } 89 90 #pragma mark - <NSURLSessionDataDelegate> 91 /** 92 * 接收到響應的時候調用 93 */ 94 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler 95 { 96 // 調用blcok,才能接受到數據 97 completionHandler(NSURLSessionResponseAllow); 98 // 初始化stream 99 self.stream = [NSOutputStream outputStreamToFileAtPath:ChaosFileFullPath append:YES]; 100 [self.stream open]; 101 102 // 獲取文件總長度 103 self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + ChaosDownloadLength; 104 105 // 接收到服務器響應的時候存儲文件的總長度到plist,實現多文件下載,先取出字典,給字典賦值最后寫入。 106 // 錯誤做法:直接寫入文件,會用這次寫入的信息覆蓋原來所有的信息 107 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist]; 108 // 字典可能為空 109 if (dict == nil) dict = [NSMutableDictionary dictionary]; 110 // 寫入文件 111 dict[ChaosFileName] = @(self.totalLength); 112 [dict writeToFile:ChaosDownloadFilesPlist atomically:YES]; 113 } 114 115 /** 116 * 接收到服務器發來的數據的時候調用 -- 有可能調用多次 117 */ 118 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 119 { 120 // 寫入數據 121 [self.stream write:[data bytes] maxLength:data.length]; 122 // 獲取已經下載的長度 123 self.downloadLength = ChaosDownloadLength; 124 // 計算進度 125 NSLog(@"%f",1.0 * self.downloadLength / self.totalLength); 126 } 127 128 /** 129 * 任務完成的時候調用 130 */ 131 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 132 { 133 NSLog(@"----finish"); 134 [self.stream close]; 135 self.stream = nil; 136 137 // 一個任務對應一個文件,用完清空 138 self.task = nil; 139 } 140 @end