iOS 後臺下載及管理庫

leaveslife發表於2017-09-18

說起下載第一個想起的就是ASI。一年前接手的新專案是核心功能是視訊相關業務,在修改和解決視訊下載相關的問題的時候讓我體會到了ASI的下載的強大。後來新需求需要視訊後臺下載,使用NSURLSession的時候,更加深刻的體會到了ASI的強大好用。

後來替換下載的時候的原因:

  1. ASI開啟後臺下載功能,在iOS10的裝置上,只能下載三分鐘(iOS10之前是10分鐘),然後就處於休眠狀態
  2. AFN下載也是三分鐘(iOS10之前是10分鐘)
  3. 測試後臺下載的時候,不要用模擬器,使用用真機。模擬器APP處於後臺時不會休眠。

關於休眠/喚醒時間

  1. [self.request setShouldContinueWhenAppEntersBackground:true];這是ASI啟動後臺下載的程式碼,self.request的型別為ASIHTTPRequest,這是其中一種啟動後臺的方法,在APP處於後臺時,不會立馬處於休眠狀態。在iOS10之前是10分鐘後處於休眠,iOS10之後3分鐘後處於休眠。 具體實現程式碼:

    - (void)setUpBackgroundTask:(UIApplication *)application{
        self.backgroundTask = [application beginBackgroundTaskWithName:@"bg1" expirationHandler:^{
            [self invalidateTimer];
            [application endBackgroundTask:self.backgroundTask];
            self.backgroundTask = UIBackgroundTaskInvalid;
        }];
    }
    //獲取剩餘時間
    [UIApplication sharedApplication].backgroundTimeRemaining);
    複製程式碼
  2. 通過YCDownloadSession,沒有處理completionHandler,在iOS11的裝置,經過測試,會一直處於喚醒狀態。如下圖,沒有處理completionHandler會一直處於喚醒狀態,2238s大概是37分鐘(手動停止的),一直處於後臺喚醒。

    未處理completionHandler測試

  3. 細節:當APP首次在後臺下載檔案時進入後臺,下載執行緒會被系統接管處於一直下載的狀態,但是APP的處於休眠狀態,UI和日誌輸出全部停止。當首個下載任務下載完成之後,AppDelegate的代理方法,回撥completedHander,APP被喚醒,日誌和UI全部恢復正常執行。上面經過iOS11裝置測試,只要不執行completionHandler,APP在後臺37分鐘時還在處於喚醒狀態,暫且認為:只要不執行completionHandler APP會一直處於喚醒狀態

  4. 還有一種喚醒APP的方法是通過push,這個功能也比較強大,伺服器可以控制後臺的app處於喚醒狀態

  5. iOS 11的裝置,在手動殺死程式後,下載任務將不再繼續進行。在奔潰或者exit(0)將會繼續下載。注意在YCDownloadSession初始化的時候,預設把程式退出後繼續下載的任務給暫停了,所以下圖測試效果中,進入App後,快取列表的視訊是暫停狀態。下圖iOS10.3.2測試效果。

    iOS10.3.2測試效果

NSURLSession的特點簡介

通過NSURLSession建立的後臺下載任務,保證了APP在後臺或者退出的狀態下,依然能進行下載任務,下載完成後通過喚醒APP,來將下載完成的資料儲存到特定的位置。

  1. 在APP處於後臺、鎖屏狀態下依然能後下載
  2. 最強大的是:APP在手動退出以及閃退後的狀態下依然能夠進行下載任務

NSURLSession

建立下載session

NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
        NSString *identifier = [NSString stringWithFormat:@"%@.BackgroundSession", bundleId];
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
        session = [NSURLSession sessionWithConfiguration:sessionConfig
                                                delegate:self
                                           delegateQueue:[NSOperationQueue mainQueue]];

複製程式碼
  1. 在建立下載session的時候,需要一個下載標識,該標識需要在整個個系統內保證唯一,所以使用APP的bundle id。
  2. sessionConfig.allowsCellularAccess 控制是否可以通過蜂窩網路下載

當APP手動退出或者閃退後,重新啟動時獲取正在下載的tasks

NSMutableDictionary *dictM = [self.downloadSession valueForKey:@"tasks"];
[dictM enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
           
}];

複製程式碼

AppDelegate後臺下載回撥

當APP處於後臺下載狀態時,需要處理下載完成後的資料的回撥,這裡就涉及了一個AppDelegate中的一個特別重要的回撥

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler{
    NSLog(@"%s", __func__);
}

複製程式碼

該代理方法使用場景分析(詳細分析過程見下面):

  1. 不實現該代理方法,手動進入App呼叫相關代理方法,部分情況下異常
  2. 實現該方法,不執行completionHandler,某一下載任務完成後喚醒App,繼續其它下載任務,異常
  3. 實現該方法,執行completionHandler,某一下載任務完成後喚醒App,繼續其它下載任務,一切正常

相關操作

  1. 建立下載任務:其實通過session建立的任務是NSURLSessionDownloadTask的子類__NSCFBackgroundDownloadTask,是蘋果的私有API。

    NSURL *downloadURL = [NSURL URLWithString:downloadURLString];
       NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
       NSURLSessionDownloadTask *downloadTask = [self.downloadSession downloadTaskWithRequest:request];
       [downloadTask resume];
    複製程式碼
  2. 暫停下載:downloadTask有多種辦法去暫停,但是我們選擇有resume的下載方法,可以更加方便我們管理和多次暫停繼續。

    [downloadTask cancelByProducingResumeData:^(NSData * resumeData) {
    }];
    複製程式碼
  3. 繼續下載:通過resumeData繼續下載。

    NSURLSessionDownloadTask *downloadTask = [self.downloadSession downloadTaskWithResumeData:data];
    [downloadTask resume];
    
    複製程式碼
  4. 取消或者刪除下載

    [downloadTask cancel];
    複製程式碼

相關代理方法說明

  1. 下載任務開始後,下載檔案的進度回撥方法。bytesWritten 某一斷點續傳過程中已經下載的資料大小, totalBytesWritten 已經下載的檔案的大小;totalBytesExpectedToWrite當前需要下載的檔案的大小

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    
    }
    複製程式碼
  2. 下載任務繼續開始下載時的回撥方法

     - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
     didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes {
    
     }
    複製程式碼
  3. 當資源發生重定向時回撥的方法。NSURLSession內部自己處理定向回撥

    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{
    
    }
    複製程式碼

    講個小故事:去年元旦左右,我們是使用ASI下載視訊的,甘肅的一個使用者反饋,視訊死活不能下載。但是我們在公司網路,國外VPN,4G,家裡的網路測試下載沒有問題,並且進行n次的測試,沒有復現該問題,後來只能只作罷。然後過年回家,臘月30到家的,和家人吃年夜飯過後,看春晚,實在沒意思,突然想起這個問題,然後就測試程式碼,我去,還真的是個必現的bug,死活下載不了。卡斷點,除錯一會後,發現下載資源URL被302重定向了,然後我們專案中的以前整合ASI,並不支援重定向,最後在失敗的回撥用,判斷了下,如果是重定向,拿到新的URL重新去下載,想想也是坑啊。 下載的資源經過cdn加速後,猜測cdn在不同的region做了不同的處理。然後北京沒事,其它的地域有可能重定向了。當時改完代後有點小開心, 因為怕同事說我猿氣太重,所以在公司提交的程式碼...

  4. 當下載任務完成後的代理回撥方法,回撥的引數location是下載完成後的檔案,在沙盒當中存在的路徑

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location{
      
    }
    
    複製程式碼
  5. 假如在後臺下載完成的回撥,會觸發該回撥方法。

    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    }
    複製程式碼
  6. 下載失敗後的回撥。暫停,停止,失敗都會觸發。區別是:正常狀態下的暫停會回撥resumeData,如果resumeData不為空的話我們需要儲存該資料,方便下次繼續。

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error{
    } 
    複製程式碼

後臺下載相關回撥

AppDelegate 回撥方法3種情況分析:

實現代理 執行completionHandler 現象
1 部分情況下異常
2 部分情況下異常
3 一切正常

1. 不實現代理方法 當不實現AppDelegate代理方法的時候,簡單的下載是沒有任何問題。NSURLSession下載完成後相關代理方法執行順序如圖:

代理呼叫順序

只有手動將後臺的App進入前臺後會呼叫成功的回撥,處理相關的資料。假如有3個下載任務則會回撥3次URLSession:downloadTask:didFinishDownloadingToURL:方法。

缺點和問題:

  • 如果下載完成後,不進入前臺或者手動殺死程式,則丟失下載資料
  • 等待下載的任務無法繼續下載
  • 下載完成後,在後臺無法使用本地通知

2. 實現代理方法,不執行completionHandler

AppDelegate 方法-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler completionHandler 作用:

首先看蘋果SDK的解釋:

// Applications using an NSURLSession with a background configuration may be launched or resumed in the background in order to handle the // completion of tasks in that session, or to handle authentication. This method will be called with the identifier of the session needing // attention. Once a session has been created from a configuration object with that identifier, the session's delegate will begin receiving // callbacks. If such a session has already been created (if the app is being resumed, for instance), then the delegate will start receiving // callbacks without any action by the application. You should call the completionHandler as soon as you're finished handling the callbacks.

最後兩句是說明completionHandler的,大概意思是:回撥callbacks,只要session建立將會開始接收到。回撥callbacks沒有對你的應用程式進行任何的處理,一旦完成處理callbacks,你應該呼叫completionHandler。

英語不好感覺說的雲裡霧裡的。callbacks應該是暫停繼續任務時受到的代理方法。說的是我們完成下載任務後應該呼叫completionHandler

completionHandler作用測試代理:

#pragma mark - test code
- (void)applicationWillResignActive:(UIApplication *)application{
    
    [self testTimer];
    NSLog(@"%s",__func__);
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [self.timer invalidate];
    self.timer = nil;
    self.duration = 0;
    NSLog(@"%s", __func__);
}


- (void)testTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerRun) userInfo:nil repeats:true];
    [self.timer fire];
}

- (void)timerRun {
    _duration += 1;
    NSLog(@"%zd", _duration);
}

複製程式碼

completionHandler具體作用: 這個回撥的作用有點牛皮。通過上面代理可以測試出completionHandler,在第一個後臺下載任務完成時回撥,這時後臺App已經被喚醒,定時器開始輸出計時秒數。然後其它的下載任務完成時不會再次回撥該方法。所有下載任務完成時,沒有處理completionHandler計時器繼續執行。 呼叫執行時completionHandler計時器停止執行,App繼續處於休眠狀態。

雖然不知道completionHandler做了哪些處理,但是通過測試現象得出大概的作用。他用來控制後臺的App被喚醒後繼續處於休眠狀態,節約系統資源。

所以不執行completionHandler App如果不重新啟動,處於後臺時會一直在執行狀態。下載任務正常。

3. 實現代理方法,執行completionHandler 上面我們分析了completionHandler大概作用。所以所有後臺任務下載完成後呼叫completionHandler,是App處於正常的狀態。相關代理呼叫順序:

代理呼叫順序

completionHandler呼叫時機: 所有的下載任務下載完成後呼叫。感興趣的可以看YCDowloadSession下載庫對completionHandler的處理邏輯。

YCDownloadSession

YCDownloadSession是我寫的一個視訊後臺下載的庫。裡面擁有我對視訊下載的詳細的處理過程和管理的庫。

該視訊下載庫主要有四個核心類:YCDownloadSession,YCDownloadTask,YCDownloadItem,YCDownloadManager

  1. YCDownloadSession:對NSURLSession的進一步分裝,是一個單例,所有的下載任務都是由其生成和管理。是最主要的核心類。實現了下載的代理方法,通過一個可下載的url,生成一個YCDownloadTask,並且將該task的所有資料進行實時儲存。
  2. YCDownloadTask 將YCDownloadSession裡的代理方法進一步封裝和擴充套件,儲存session生成和所需要的一些下載資訊和資料。
  3. YCDownloadItem 存放需要下載的視訊的資訊
  4. YCDownloadManager 管理下載視訊操作,生成一個YCDownloadItem,並且實時儲存相關資訊(下載狀態,檔案大小,已下載檔案大小,以及其它的需要和UI互動的資料),然後呼叫YCDownloadSession去下載該視訊。

圖解

YCDownloadSession結構圖解

YCDownloadSession和YCDownloadTask是兩個核心類。與YCDownloadManager和YCDownloadItem相互獨立。大家和可以通過YCDownloadSession和YCDownloadTask自定義需要的下載管理類的資訊類。

使用效果圖

  1. 單檔案下載測試

單檔案下載

  1. 多視訊下載測試

多視訊下載

  1. 通知

通知

用法

  1. AppDelegate設定後臺下載成功回撥方法

    -(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler{
        [[YCDownloadSession downloadSession] addCompletionHandler:completionHandler];
    }
    
    複製程式碼

    如果想要多個任務在後臺同時進行,必須要進行設定上述的代理方法。YCDownloadSession內部會處理該回撥方法(completionHandler的作用將會在blog裡詳細說明),內部處理邏輯:

     //等task delegate方法執行完成後去判斷該邏輯
     //URLSessionDidFinishEventsForBackgroundURLSession 方法在後臺執行一次,所以在此判斷執行completedHandler
     if (status == YCDownloadStatusFinished) {
         
         if ([self allTaskFinised]) {
             [[NSNotificationCenter defaultCenter] postNotificationName:kDownloadAllTaskFinishedNoti object:nil];
             //所有的任務執行結束之後呼叫completedHanlder
             if (self.completedHandler) {
                 NSLog(@"completedHandler");
                 self.completedHandler();
                 self.completedHandler = nil;
             }
         }
      
     }
    複製程式碼
  2. 直接使用YCDownloadSession下載檔案

    self.downloadURL = @"http://dldir1.qq.com/qqfile/QQforMac/QQ_V6.0.1.dmg";
    
    - (void)start {
        [[YCDownloadSession downloadSession] startDownloadWithUrl:self.downloadURL delegate:self saveName:nil];
    }
    - (void)resume {
        [[YCDownloadSession downloadSession] resumeDownloadWithUrl:self.downloadURL delegate:self saveName:nil];
    }
    
    - (void)pause {
        [[YCDownloadSession downloadSession] pauseDownloadWithUrl:self.downloadURL];
    }
    
    - (void)stop {
        [[YCDownloadSession downloadSession] stopDownloadWithUrl:self.downloadURL];
    }
    	
    //代理
    - (void)downloadProgress:(YCDownloadTask *)task downloadedSize:(NSUInteger)downloadedSize fileSize:(NSUInteger)fileSize {
        self.progressLbl.text = [NSString stringWithFormat:@"%f",(float)downloadedSize / fileSize * 100];
    }
    
    	
    - (void)downloadStatusChanged:(YCDownloadStatus)status downloadTask:(YCDownloadTask *)task {
        if (status == YCDownloadStatusFinished) {
            self.progressLbl.text = @"download success!";
        }else if (status == YCDownloadStatusFailed){
            self.progressLbl.text = @"download failed!";
        }
    }
    
    複製程式碼
  3. 使用自定義的管理類(YCDownloadManager 視訊型別檔案專用下載管理類)下載。假如視訊下載完成,自定義儲存名稱,那麼使用fileId來標識。如果fileId為空使用下載URL的MD5的值來儲存

    /**
     開始/建立一個後臺下載任務。downloadURLString作為整個下載任務的唯一標識。
     下載成功後用downloadURLString的MD5的值來儲存
     檔案字尾名取downloadURLString的字尾名,[downloadURLString pathExtension]
    
     */
    + (void)startDownloadWithUrl:(NSString *)downloadURLString fileName:(NSString *)fileName imageUrl:(NSString *)imagUrl;
    
    /**
     開始/建立一個後臺下載任務。fileId作為整個下載任務的唯一標識。
     下載成功後用fileId來儲存, 要確保fileId唯一
     檔案字尾名取downloadURLString的字尾名,[downloadURLString pathExtension]
     
     */
    + (void)startDownloadWithUrl:(NSString *)downloadURLString fileName:(NSString *)fileName imageUrl:(NSString *)imagUrl fileId:(NSString *)fileId;
    
    
    複製程式碼
  4. 蜂窩煤是否允許下載的方法(YCDownloadSession, YCDownloadManager)

    YCDownloadSession: 
    /**
     是否允許蜂窩煤網路下載,以及網路狀態變為蜂窩煤是否允許下載,必須把所有的downloadTask全部暫停,然後重新建立。否則,原先建立的
     下載task依舊在網路切換為蜂窩煤網路時會繼續下載
     
     @param isAllow 是否允許蜂窩煤網路下載
     */
    - (void)allowsCellularAccess:(BOOL)isAllow;
    
    YCDownloadManager:
    /**
     獲取當前是否允許蜂窩煤訪問狀態
     */
    - (BOOL)isAllowsCellularAccess;
    複製程式碼
  5. 設定最大同時進行下載的任務數

    YCDownloadSession: 
    /**
     設定下載任務的個數,最多支援3個下載任務同時進行。
     NSURLSession最多支援5個任務同時進行
     但是5個任務,在某些情況下,部分任務會出現等待的狀態,所有設定最多支援3個
     */
    @property (nonatomic, assign) NSInteger maxTaskCount;
    
    
    
    YCDownloadManager:
    /**
     設定下載任務的個數,最多支援3個下載任務同時進行。
     */
    + (void)setMaxTaskCount:(NSInteger)count;
    複製程式碼
  6. 下載完成的通知

    • 本地通知(YCDownloadManager實現):

      /**
       本地通知的開關,預設是false,可以根據通知名稱自定義通知型別
       */
      + (void)localPushOn:(BOOL)isOn;
      複製程式碼
    • 當前session中所有的任務下載完成的通知。 不包括失敗、暫停的任務: kDownloadAllTaskFinishedNoti

    • 某一的任務下載完成的通知object為YCDownloadItem物件:kDownloadTaskFinishedNoti

  7. 某一任務下載的狀態發生變化的通知: kDownloadStatusChangedNoti 主要用於狀態改變後,及時儲存下載資料資訊。

GitHub連線 github.com/onezens/YCD…

歡迎各位關注該庫,如果你有任何問題請issues我,將會隨時更新新功能和解決存在的問題。

遇到的一些問題

這裡總結下載開發YCDownloadSession下載庫中碰到的一些問題

蘋果下載相關SDK關係圖

  1. 下載資源重定向的問題 YCDownloadSession 內部標識一下下載task的時候,使用的下載資源的URL來標識。如果該資源被301/302重定向到一個另一個URL後,會存在兩個URL。標識用的URL在代理回撥的NSURLSessionTask或者NSURLSessionDownloadTask的currentRequest中取的url,這樣就出現了一個問題,重定向後通過URL拿不到下載的task;originalRequest屬性可以拿到重定向前的URL使用該屬性解決這個問題。蘋果下載相關SDK關係圖可看它們之間關係。

  2. 斷點續傳 NSURLSessionDownloadTask的斷點續傳是由其內部自己控制實現。在暫停某一下載任務的時候有兩個方法:

    • cancel內部自己控制斷點續傳資料,拿到對應task可以繼續下載。如果拿不到,不可繼續。
    • cancelByProducingResumeData:^(NSData * resumeData) {}通過session繼續下載[downloadSession downloadTaskWithResumeData:data],需要自己儲存處理resumeData,可以滿足很多情況下的續傳。
  3. 部分下載資源不可斷點續傳 YCDownloadSession Demo中的測試用的下載資源來自百度視訊,可以正常下載。網易視訊的資源和部分響應頭不完整的資源在暫停下載之後拿不到resumeData而回撥失敗的情況。

    • 百度視訊資源:https://vd1.bdstatic.com/mda-hiqmm8s10vww26sx/mda-hiqmm8s10vww26sx.mp4\?playlist\=%5B%22hd%22%5D\&auth_key\=1506158514-0-0-6cde713ec6e6a15bd856fbb4f2564658\&bcevod_channel\=searchbox_feed 響應頭: 通過Mac Terminal自帶的curl命令獲取響應頭 curl -I https://vd1.bdstatic.com/mda-hiqmm8s10vww26sx/mda-hiqmm8s10vww26sx.mp4\?playlist\=%5B%22hd%22%5D\&auth_key\=1506158514-0-0-6cde713ec6e6a15bd856fbb4f2564658\&bcevod_channel\=searchbox_feed
    HTTP/1.1 200 OK
    Server: bfe/1.0.8.13-sslpool-patch
    Date: Tue, 10 Oct 2017 07:43:02 GMT
    Content-Type: video/mp4
    Content-Length: 19727666
    Connection: keep-alive
    ETag: "125b8b749037921ccd03120fb0f90189"
    Last-Modified: Fri, 15 Sep 2017 09:42:18 GMT
    Accept-Ranges: bytes
    Content-MD5: EluLdJA3khzNAxIPsPkBiQ==
    x-bce-debug-id: MTAuMTgxLjk5LjE0OlNhdCwgMTYgU2VwIDIwMTcgMTE6MzA6MzYgQ1NUOjE4MzY0NTgzMjM=
    x-bce-request-id: b3c7857b-b71a-4cd4-ab98-8676319dc8cb
    x-bce-storage-class: STANDARD
    Ohc-Response-Time: 1 0 0 0 0 94
    Access-Control-Allow-Origin: *
    Cache-Control: max-age=2592000
    複製程式碼
    • 網易視訊資源:http://flv2.bn.netease.com/videolib3/1706/07/gDNOH8458/HD/gDNOH8458-mobile.mp4 響應頭:
    curl -I http://flv2.bn.netease.com/videolib3/1706/07/gDNOH8458/HD/gDNOH8458-mobile.mp4
    
    HTTP/1.1 200 OK
    Expires: Thu, 09 Nov 2017 07:00:42 GMT
    Date: Tue, 10 Oct 2017 07:00:42 GMT
    Server: nginx
    Content-Length: 43260722
    Cache-Control: max-age=2592000
    Content-Type: video/mp4
    Via: 1.1 zhshx117:1 (Cdn Cache Server V2.0)[11 200 2], 1.1 xxz195:3 (Cdn Cache Server V2.0)[94 200 2], 1.1 PStjdgdx3jx152:4 (Cdn Cache Server V2.0)[154 200 2]
    Connection: keep-alive
    cache: state
    cdn-user-ip: 101.96.129.122
    cdn-ip: 42.81.28.152
    cdn-source: chinanetcenter
    複製程式碼
    • 響應頭異常視訊資源:https://www.zmzfile.com:9043/rt/route\?fileid\=152260954bdfa322725ba58df2ab1e2c2e3a6050 響應頭:
    curl -I https://www.zmzfile.com:9043/rt/route\?fileid\=152260954bdfa322725ba58df2ab1e2c2e3a6050
    
    HTTP/1.1 302 Found
    Server: nginx/1.12.1
    Date: Tue, 10 Oct 2017 07:53:23 GMT
    Content-Type: text/plain; charset=utf-8
    Connection: keep-alive
    Location: http://175.6.228.3:9021?id=152260954bdfa322725ba58df2ab1e2c2e3a6050
    Ts: 1716489
    
    繼續取302資源的響應頭
    curl -I http://175.6.228.3:9021\?id\=152260954bdfa322725ba58df2ab1e2c2e3a6050
    Ts: 1716489
    curl: (52) Empty reply from server
    
    Xcode 輸出NSURLSessionDownloadTask的response資訊:
    <NSHTTPURLResponse: 0x13cee64d0> { URL: [http://60.211.203.204:9031?id=152260954bdfa322725ba58df2ab1e2c2e3a6050](http://60.211.203.204:9031/?id=152260954bdfa322725ba58df2ab1e2c2e3a6050)} {
     status code: 200,
     headers {
      Connection = Close;
      "Content-Length" = 247272762;
      Server = p4pcacher;
    } }
    複製程式碼

百度視訊是支援斷點續傳,網易視訊和第三種不支援斷點續傳。不支援斷點續傳的原因是資源的響應頭裡面沒有Accept-Ranges: bytesETag這個兩個欄位,所以在點選暫停的時候,蘋果SDK回撥失敗。關於網路資源斷點續傳的介紹:blog.chinaunix.net/uid-2451251…

相關文章