iOS大檔案斷點續傳

黑暗森林的歌者發表於2018-02-26

定義

@interface FileDownloadManager ()<NSURLSessionDataDelegate>
/**
 *  下載任務
 */
@property (nonatomic, strong) NSURLSessionDataTask      *downloadTask;
/**
 *  session
 */
@property (nonatomic, strong) NSURLSession              *session;
/**
 *  下載物件流
 */
@property (nonatomic, strong) NSOutputStream            *stream;
/**
 *  檔案總長度
 */
@property (nonatomic, assign) float                     totalLength;
/**
 *  下載中的資料模型
 */
@property (nonatomic, strong) FileDownloadModel         *myDownloadModel;
@end
複製程式碼

建立session

- (NSURLSession *)session {
    if (!_session) {
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
    }
    return _session;
}
複製程式碼

建立請求

- (void)initDownloadTast {
    [self initDownloadSream:@"下載檔案的名字"];
    
    //建立請求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString@"下載連結"]];
    //設定請求頭
    //請求頭的內容是已經下載的檔案大小
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-", [[[NSFileManager defaultManager] attributesOfItemAtPath:[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", @"儲存檔案的名字"]] error:nil][NSFileSize] integerValue]];
    [request setValue:range forHTTPHeaderField:@"Range"];
    
    //建立一個data任務
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        self.downloadTask = [self.session dataTaskWithRequest:request];
        [self.downloadTask resume];
    });
}
複製程式碼

建立下載流

- (void)initDownloadSream:(NSString *)fileName {
    if (!_stream) {
        _stream = [NSOutputStream outputStreamToFileAtPath:[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", fileName]] append:YES];
    }
}
複製程式碼

NSURLSessionDataDelegate

/**
 * 接受到響應
**/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
    //開啟流
    [self.stream open];
    // 獲取伺服器請求, 返回未下載資料的長度,需要加上已經下載資料長度才會得到資料總長度
    self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + [[[NSFileManager defaultManager] attributesOfItemAtPath:[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", @"下載檔名字"]] error:nil][NSFileSize] integerValue];
    completionHandler(NSURLSessionResponseAllow);
}
/**
 * 接受到伺服器返回的資料(呼叫多次)
 **/
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    //寫入資料
    [self.stream write:data.bytes maxLength:data.length];
     
        dispatch_async(dispatch_get_main_queue(), ^{
              //在主執行緒上更新UI進度條
        });
}
/**
 * 請求完畢
 **/
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    [self.stream close];
    self.stream = nil;
    self.downloadTask = nil;
    if (!error) {
        //下載成功
        //處理下載下來的檔案(解壓或者轉移等等)
        //更新UI記得在主執行緒
    }
}
複製程式碼

當網路斷開的時候會呼叫請求完畢這個代理,這時候 error 是有值的,檢視其中的userInfo, 這個key @"NSLocalizedDescription", 對應的內容是@"網路連線已中斷。" (注意有句號),可以在這裡執行斷網後的操作

相關文章