iOS邊練邊學--NSURLSessionDataTask實現文件真正的斷點續傳


實現重點:

  • 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM