iOS開發-大文件下載與斷點下載思路


  • 大文件下載
    方案一:利用NSURLConnection和它的代理方法,及NSFileHandle(iOS9后不建議使用)
    相關變量:
    @property (nonatomic,strong) NSFileHandle *writeHandle;
    @property (nonatomic,assign) long long totalLength;
    1>發送請求
    // 創建一個請求
        NSURL *url = [NSURL URLWithString:@""];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // 使用NSURLConnection發起一個異步請求
        [NSURLConnection connectionWithRequest:request delegate:self];

    2>在代理方法中處理服務器返回的數據

    /** 在接收到服務器的響應時調用下面這個代理方法
        1.創建一個空文件
        2.用一個句柄對象關聯這個空文件,目的是方便在空文件后面寫入數據
    */
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
    {
        // 創建文件路徑
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
        NSString *filePath = [caches stringByAppendingPathComponent:@"videos.zip"];
        
        // 創建一個空的文件到沙盒中
        NSFileManager *mgr = [NSFileManager defaultManager];
        [mgr createFileAtPath:filePath contents:nil attributes:nil];
        
        // 創建一個用來寫數據的文件句柄
        self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
        
        // 獲得文件的總大小
        self.totalLength = response.expectedContentLength;
    }
    
    /** 在接收到服務器返回的文件數據時調用下面這個代理方法
        利用句柄對象往文件的最后面追加數據
     */
    - (void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
    {
        // 移動到文件的最后面
        [self.writeHandle seekToEndOfFile];
        
        // 將數據寫入沙盒
        [self.writeHandle writeData:data];
    }
    
    /**
        在所有數據接收完畢時,關閉句柄對象
     */
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        // 關閉文件並清空
        [self.writeHandle closeFile];
        self.writeHandle = nil;
    }

    方案二:使用NSURLSession的NSURLSessionDownloadTask和NSFileManager

    NSURLSession *session = [NSURLSession sharedSession];
        NSURL *url = [NSURL URLWithString:@""];
        // 可以用來下載大文件,數據將會存在沙盒里的tmp文件夾
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            // location :臨時文件存放的路徑(下載好的文件)
            
            // 創建存儲文件路徑
            NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
            // response.suggestedFilename:建議使用的文件名,一般跟服務器端的文件名一致
            NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
            
            /**將臨時文件剪切或者復制到Caches文件夾
             AtPath :剪切前的文件路徑
             toPath :剪切后的文件路徑
             */
            NSFileManager *mgr = [NSFileManager defaultManager];
            [mgr moveItemAtPath:location.path toPath:file error:nil];
        }];
        [task resume];

    方案三:使用NSURLSessionDownloadDelegate的代理方法和NSFileManger

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        // 創建一個下載任務並設置代理
        NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        
        NSURL *url = [NSURL URLWithString:@""];
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];
        [task resume];
    }
    
    #pragma mark - 
    /**
        下載完畢后調用
        參數:lication 臨時文件的路徑(下載好的文件)
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location{
        // 創建存儲文件路徑
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
        // response.suggestedFilename:建議使用的文件名,一般跟服務器端的文件名一致
        NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        
        /**將臨時文件剪切或者復制到Caches文件夾
         AtPath :剪切前的文件路徑
         toPath :剪切后的文件路徑
         */
        NSFileManager *mgr = [NSFileManager defaultManager];
        [mgr moveItemAtPath:location.path toPath:file error:nil];
    }
    
    /**
        每當下載完一部分時就會調用(可能會被調用多次)
        參數:
            bytesWritten 這次調用下載了多少
            totalBytesWritten 累計寫了多少長度到沙盒中了
            totalBytesExpectedToWrite 文件總大小
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
        // 這里可以做些顯示進度等操作
    }
    
    /**
        恢復下載時使用
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
     didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes
    {
        // 用於斷點續傳
    }

     

  • 斷點下載
    方案一:
    1>在方案一的基礎上新增兩個變量和按扭
    @property (nonatomic,assign) long long currentLength;
    @property (nonatomic,strong) NSURLConnection *conn;

    2>在接收到服務器返回數據的代理方法中添加如下代碼

        // 記錄斷點,累計文件長度
        self.currentLength += data.length;

    3>點擊按鈕開始(繼續)或暫停下載

    - (IBAction)download:(UIButton *)sender {
        
        sender.selected = !sender.isSelected;
        
        if (sender.selected) { // 繼續(開始)下載
            NSURL *url = [NSURL URLWithString:@""];
            // ****關鍵點是使用NSMutableURLRequest,設置請求頭Range
            NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:url];
            
            NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
            [mRequest setValue:range forHTTPHeaderField:@"Range"];
            
            // 下載
            self.conn = [NSURLConnection connectionWithRequest:mRequest delegate:self];
        }else{
            [self.conn cancel];
            self.conn = nil;
        }
    }

    4>在接受到服務器響應執行的代理方法中第一行添加下面代碼,防止重復創建空文件

     if (self.currentLength)  return;

     

    方案二:使用NSURLSessionDownloadDelegate的代理方法
    所需變量

    @property (nonatomic,strong) NSURLSession *session;
    @property (nonatomic,strong) NSData *resumeData; //包含了繼續下載的開始位置和下載的url
    @property (nonatomic,strong) NSURLSessionDownloadTask *task;

    方法

    // 懶加載session
    - (NSURLSession *)session
    {
        if (!_session) {
            NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
            self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        }
        return _session;
    }
    
    - (IBAction)download:(UIButton *)sender {
        
        sender.selected = !sender.isSelected;
        if (self.task == nil) { // 開始(繼續)下載
            if (self.resumeData) { // 原先有數據則恢復
                [self resume];
            }else{
                [self start]; // 原先沒有數據則開始
            }
        }else{ // 暫停
            [self pause];
        }
    }
    
    // 從零開始
    - (void)start{
        NSURL *url = [NSURL URLWithString:@""];
        self.task = [self.session downloadTaskWithURL:url];
        [self.task resume];
    }
    
    // 暫停
    - (void)pause{
        __weak typeof(self) vc = self;
        [self.task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
            //resumeData : 包含了繼續下載的開始位置和下載的url
            vc.resumeData = resumeData;
            vc.task = nil;
        }];
    }
    
    // 恢復
    - (void)resume{
        // 傳入上次暫停下載返回的數據,就可以回復下載
        self.task = [self.session downloadTaskWithResumeData:self.resumeData];
        // 開始任務
        [self.task resume];
        // 清空
        self.resumeData = nil;
    }
    
    #pragma mark - NSURLSessionDownloadDelegate
    /**
        下載完畢后調用
        參數:lication 臨時文件的路徑(下載好的文件)
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location{
        // 創建存儲文件路徑
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
        // response.suggestedFilename:建議使用的文件名,一般跟服務器端的文件名一致
        NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        
        /**將臨時文件剪切或者復制到Caches文件夾
         AtPath :剪切前的文件路徑
         toPath :剪切后的文件路徑
         */
        NSFileManager *mgr = [NSFileManager defaultManager];
        [mgr moveItemAtPath:location.path toPath:file error:nil];
    }
    
    /**
        每當下載完一部分時就會調用(可能會被調用多次)
        參數:
            bytesWritten 這次調用下載了多少
            totalBytesWritten 累計寫了多少長度到沙盒中了
            totalBytesExpectedToWrite 文件總大小
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
        // 這里可以做些顯示進度等操作
    }
    
    /**
        恢復下載時使用
     */
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
     didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes
    {
    }

     

     

     




免責聲明!

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



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