iOS開發中利用AFNetworking進行斷點下載

樑森森發表於2017-07-05

在iOS開發中我們如果要下載一個大的檔案可能會有斷點下載的需求即能夠對下載任務進行暫停,之後再開始下載任務,下面即將給出利用AFNetworking進行斷點下載的程式碼。核心思想:將下載的檔案的長度記錄下載,然後再次下載的時候即傳送網路請求的時候在請求頭中設定下載資料的位置。關鍵程式碼:

// 設定HTTP請求頭中的Range

        NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];

        [request setValue:range forHTTPHeaderField:@"Range"];



全部程式碼:

.m檔案中的程式碼:



#import "AFNetworkingOfflineResumeDownloadFileViewController.h"

#import <AFNetworking.h>


@interface AFNetworkingOfflineResumeDownloadFileViewController ()


/** 下載進度條 */

@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

/** 下載進度條Label */

@property (weak, nonatomic) IBOutlet UILabel *progressLabel;


/** AFNetworking斷點下載(支援離線)需用到的屬性 **********/

/** 檔案的總長度 */

@property (nonatomic, assign) NSInteger fileLength;

/** 當前下載長度 */

@property (nonatomic, assign) NSInteger currentLength;

/** 檔案控制程式碼物件 */

@property (nonatomic, strong) NSFileHandle *fileHandle;


/** 下載任務 */

@property (nonatomic, strong) NSURLSessionDataTask *downloadTask;

/* AFURLSessionManager */

@property (nonatomic, strong) AFURLSessionManager *manager;


@end


@implementation AFNetworkingOfflineResumeDownloadFileViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    

    self.navigationItem.title = @"AFNetworking斷點下載(支援離線)";

}


/**

 * manager的懶載入

 */

- (AFURLSessionManager *)manager {

    if (!_manager) {

        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

        // 1. 建立會話管理者

        _manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

    }

    return _manager;

}


/**

 * downloadTask的懶載入

 */

- (NSURLSessionDataTask *)downloadTask {

    if (!_downloadTask) {

        // 建立下載URL

        NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];

        

        // 2.建立request請求

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

        

        // 設定HTTP請求頭中的Range

        NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];

        [request setValue:range forHTTPHeaderField:@"Range"];

        

        __weak typeof(self) weakSelf = self;

        _downloadTask = [self.manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

            NSLog(@"dataTaskWithRequest");

            

            // 清空長度

            weakSelf.currentLength = 0;

            weakSelf.fileLength = 0;

            

            // 關閉fileHandle

            [weakSelf.fileHandle closeFile];

            weakSelf.fileHandle = nil;

            

        }];

        

        // 第一次收到伺服器的響應的block

        [self.manager setDataTaskDidReceiveResponseBlock:^NSURLSessionResponseDisposition(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSURLResponse * _Nonnull response) {

            NSLog(@"NSURLSessionResponseDisposition");

            if (weakSelf.currentLength){

                return NSURLSessionResponseAllow;

            }else{

                // 獲得下載檔案的總長度:請求下載的檔案長度 + 當前已經下載的檔案長度

//                weakSelf.fileLength = response.expectedContentLength + self.currentLength;

                weakSelf.fileLength = response.expectedContentLength;

                // 沙盒檔案路徑

                NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"QQ_V5.4.0.dmg"];

                

                NSLog(@"File downloaded to: %@",path);

                

                // 建立一個空的檔案到沙盒中

                NSFileManager *manager = [NSFileManager defaultManager];

                

                if (![manager fileExistsAtPath:path]) {

                    // 如果沒有下載檔案的話,就建立一個檔案。如果有下載檔案的話,則不用重新建立(不然會覆蓋掉之前的檔案)

                    [manager createFileAtPath:path contents:nil attributes:nil];

                }

                

                // 建立檔案控制程式碼

                weakSelf.fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];

                

                // 允許處理伺服器的響應,才會繼續接收伺服器返回的資料

                return NSURLSessionResponseAllow;

            }

            

        }];

        

        // 收到伺服器返回的資料的block

        [self.manager setDataTaskDidReceiveDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSData * _Nonnull data) {

            NSLog(@"setDataTaskDidReceiveDataBlock");

            

            // 指定資料的寫入位置 -- 檔案內容的最後面

            [weakSelf.fileHandle seekToEndOfFile];

            

            // 向沙盒寫入資料

            [weakSelf.fileHandle writeData:data];

            

            // 拼接檔案總長度

            weakSelf.currentLength += data.length;

            

            // 獲取主執行緒,不然無法正確顯示進度。

            NSOperationQueue* mainQueue = [NSOperationQueue mainQueue];

            [mainQueue addOperationWithBlock:^{

                // 下載進度

                if (weakSelf.fileLength == 0) {

                    weakSelf.progressView.progress = 0.0;

                    weakSelf.progressLabel.text = [NSString stringWithFormat:@"當前下載進度:00.00%%"];

                } else {

                    weakSelf.progressView.progress1.0 * weakSelf.currentLength / weakSelf.fileLength;

                    weakSelf.progressLabel.text = [NSString stringWithFormat:@"當前下載進度:%.2f%%",100.0 * weakSelf.currentLength / weakSelf.fileLength];

                }

               

            }];

        }];

    }

    return _downloadTask;

}


/**

 * 點選按鈕 -- 使用AFNetworking斷點下載(支援離線)

 */

- (IBAction)OfflinResumeDownloadBtnClicked:(UIButton *)sender {

    // 按鈕狀態取反

    sender.selected = !sender.isSelected;

    

    if (sender.selected) { // [開始下載/繼續下載]

        // 沙盒檔案路徑

        NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"QQ_V5.4.0.dmg"];

        

        NSInteger currentLength = [self fileLengthForPath:path];

        if (currentLength > 0) {  // [繼續下載]

            self.currentLength = currentLength;

        }

        

        [self.downloadTask resume];

        

    } else {

        // 暫停下載

        [self.downloadTask suspend];

        self.downloadTask = nil;

    }

}


/**

 * 獲取已下載的檔案大小

 */

- (NSInteger)fileLengthForPath:(NSString *)path {

    NSInteger fileLength = 0;

    NSFileManager *fileManager = [[NSFileManager alloc] init]; // default is not thread safe

    if ([fileManager fileExistsAtPath:path]) {

        NSError *error = nil;

        NSDictionary *fileDict = [fileManager attributesOfItemAtPath:path error:&error];

        if (!error && fileDict) {

            fileLength = [fileDict fileSize];

        }

    }

    return fileLength;

}



@end



相關文章