iOS 后台下载及管理库

作者: 7b33a23272c4 | 来源:发表于2017-09-18 16:55 被阅读2273次

    说起下载第一个想起的就是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];
      
    1. 暂停下载:downloadTask有多种办法去暂停,但是我们选择有resume的下载方法,可以更加方便我们管理和多次暂停继续。

      [downloadTask cancelByProducingResumeData:^(NSData * resumeData) {
      }];
      
    2. 继续下载:通过resumeData继续下载。

      NSURLSessionDownloadTask *downloadTask = [self.downloadSession downloadTaskWithResumeData:data];
      [downloadTask resume];
      
      
    3. 取消或者删除下载

      [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连接
    https://github.com/onezens/YCDownloadSession

    欢迎各位关注该库,如果你有任何问题请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回调失败。关于网络资源断点续传的介绍:http://blog.chinaunix.net/uid-24512513-id-3391252.html

    相关文章

      网友评论

      • 余大可:请问作者我怎么区分同一个用户多账号下载同一个资源呢?
        7b33a23272c4:新版的下载库可以将所有用户区分标识设置同一个
      • ea9cb62c5c93:感谢分享!!看了几天源代码,有一些问题指出来,望修正:
        1.下载没有对非200的情况进行处理(没有对接收到响应这个回调进行处理),导致下载地址是错误的还能把错误的信息写入文件。
        2.我用的版本是YCDownloadSession(1.2.3),在初始化Session的时候并没有暂停所有下载,导致手动杀掉app会暂停,但是通过xcode杀掉或者程序crash下次启动app状态还是正在下载。
        3.类型转换有问题,content-length是long long类型,直接把已下载大小(downloadedSize)和文件大小(fileSize)用nsinter类型强转,这样导到32位机型nsinter会超出取值范围,只能下载2G以下的视频。
      • PGOne爱吃饺子:下载完成之后,没有通知提示,查看了你的代码好像代码只是实现了发出通知,但是没有地方接收通知
      • bd68039da95a:大神我遇到一个问题,就是用backgroudSession,连着xcode调试下可以后台下载,独立运行就不行是为什么啊
      • MinimalismC:调用manager里面的方法去删除下载的数据,获取本地所有下载数据,为空怎么解决?
      • 27639726f8b9:楼主,我的锁屏状态下下载不行呢,再解锁后app也重新启动了,任务也只下载了1%这样。不锁屏时后台下载没问题。请教下您可能是哪里出现了问题呢?
      • 我还只是个孩子啊:您好,请教一下:我用你封装好的工具类,如何获取当前的下载任务呢?
      • 27639726f8b9:楼主,我的需求是视频下载。但一个视频资源被后台分割为几百个URL(每个URL比方说含1M的资源)需要依次下载,最后拼接已下载的数据,那还能用你的框架了吗?
      • okerivy:群主,急救啊,麻烦看下,必现的崩溃:fearful:
        ios11.3 的bug
        demo中 单文件下载。
        先点击 start
        等5秒 点击 pause 暂停
        等3秒 点击 resume 崩溃。。。
        最终崩溃在这行 代码
        NSURLSessionDownloadTask *task = [urlSession downloadTaskWithResumeData:cData];
      • SoulmateA:大神,为什么有的视频无法下载,比如说这个视频http://portal.zhucheng360.com/1-68-6.mp4
        无法下载
      • rockyMJ:请问一下大神,后台上传是怎么实现的?也是用NSUrlSession吗?这个框架可以用来上传吗?
        rockyMJ:@LeavesLife 好的 谢谢
        7b33a23272c4:@rockyMJ 目前是不支持的,这里有上传的demo https://www.jianshu.com/p/59bcdbb1723c 。因为最近超忙,所以没时间去写了,你看看上传demo 自己改改吧
      • 梦的森林:现在遇到个问题,比如下载5个文件,3个是下载中,2个是等待,这时候进入后台,那三个下载中的文件会下载完成,但2个等待的文件就不再下载了。(不使用xcode调试)
        7b33a23272c4:@梦的森林 这个是一个重要的问题,刚刚已经解决,下载最新的试试 https://github.com/onezens/YCDownloadSession
        梦的森林:@LeavesLife 是最新的,我把demo烧到手机上,然后结束xcode,再运行demo,就不能后台下载等待的任务。如果直接把demo运行到手机上,不停掉xcode,是可以的。
        7b33a23272c4:@梦的森林 是最新的代码吗?不是的话,下载最新代码。我试了下没有问题,你出现这种问题应该是后台下载过程中的处理有问题
      • 小鱼儿喜欢花无缺:你好,我想问下后台下载在蜂窝网络下会下载吗?
        7b33a23272c4:@小鱼儿喜欢花无缺 有蜂窝开关,开着的话会下载,关掉不会下载
      • MrJ的杂货铺:杀死app后,每次都重头开始下啊,而且无法暂停
        7b33a23272c4:@MrJ的杂货铺 手动不会,闪退会
        MrJ的杂货铺:@LeavesLife 已更新,手动杀死App,还会继续下载吗?
        7b33a23272c4:@MrJ的杂货铺 更新最新版
      • 三秋故事屋:支持FTP协议吗?
      • 4eeb8b9aa518:用这种方法下载视频,如何对视频进行加密,也就是当前用户只可以自己看,不能发给他人?
        7b33a23272c4:@冰雪飞舞呀 ios 本身就有沙盒限制。不越狱,开发者不做文件共享,基本上不可能读取出视频。如果过防止越狱后,从沙盒读取缓存的加密,我认为只能从视频源去加密了。
      • 憂莜:发现了两个问题,第一,调用全部暂停方法时会把状态为Finish的item同时变为pause;第二,正在下载比如进度为30%,将程序直接杀死,再次启动应用的话进度就变为0%这样的
        憂莜:@LeavesLife 在被杀死时会走- (void)applicationWillTerminate:(UIApplication *)application这个方法,是不是可以在这个方法里做一个下载进度的保存处理
        7b33a23272c4:第一个问题已经解决了,你可以pull最新的代码试试。第二个问题,正在找完美的解决方案
        7b33a23272c4:1. 是不是在特殊情况下会复现?
        2. 目前是个老问题,进度只有在状态发生变化是时,去存储,如果一个任务没有发生过改变,被杀死,会出现下载进度为空的情况。
      • 李雷川:我测试git上最新的代码,我在下载工程中,把应用删除掉,然后重新启动重新,任务继续下载,缓存列表的下载任务没有继续更新下载状态。
        7b33a23272c4:@李雷川 YCDownloadSession  单例 初始化时默认把下载的任务给暂停了,和这个没关系。这个只是,初始化暂停后会调用的方法
        李雷川:@LeavesLife 但其实任务的内容在继续下载,下载完成后调用了方法:
        - (void)URLSession:(NSURLSession *)session
        task:(NSURLSessionTask *)task
        didCompleteWithError:(NSError *)error ,只是error里有内容。
        7b33a23272c4:@李雷川 是吧进程杀掉吧,默认启动后,把程序退出后下载的任务给暂停了,所以不会更新
      • YC_ToTo:集成后才发现只能下载视频
        YC_ToTo:@LeavesLife 折腾了一天,原因是服务器给的路径不能下载.话说框架真的挺好用,学习了.
        7b33a23272c4:下载文件直接使用YCDownloadSession下载
        7b33a23272c4:@YC_ToTo 认真看下文档的用法: (YCDownloadManager 视频类型文件专用下载管理类)
      • 怀念裸奔的童年:视频没下载完怎么播放啊,类似爱奇艺的视频下载,下载到百分之多少的时候就可以播放了
        7b33a23272c4:这个没有怎么研究,等有时间研究下。目前有个简单的思路,就是拿到没有缓存完全的文件去播放,但是估计效果不太理想,有很多细节需要去处理
      • 呵呵哈哈嘿嘿:请教下,app进入后台或者锁屏状态下,如何还能继续下载?
        7b33a23272c4:@呵呵哈哈嘿嘿 内部自己实现了,你不用关的。会用库就行,用库看文档用法就行
        呵呵哈哈嘿嘿:@LeavesLife 我有看,用了你的库,但还是不知道,怎么实现在后台或者锁屏状态下的下载
        7b33a23272c4:@呵呵哈哈嘿嘿 你仔细看下这篇文章吧,说的是怎么样实现APP进入后台或者锁屏状态下载的,包括一些细节和注意点。或者使用介绍的下载库
      • mamat:demo在xcode9 iPhone8模拟器下运行崩溃啊 点击多任务下载崩溃
        7b33a23272c4:@gufs 已经解决了,下载最新的试试
      • EvenTime:去参考下楼主实时保存进度的处理
      • 怀念裸奔的童年:很看好这个视频下载库,希望能持续更新维护,我马上有个视频下载的需求,会用这个库,如果遇到问题了请教一下哈
        7b33a23272c4:@怀念裸奔的童年 +1 , YCDownloadManager会重复创建item,我改改,:+1:
        怀念裸奔的童年:@LeavesLife /**
        点击下载
        */
        - (void)videoListCell:(VideoListInfoCell *)cell downloadVideo:(VideoListInfoModel *)model {
        NSLog(@"%@", model.mp4_url);
        if (![YCDownloadManager isDownloadWithUrl:model.mp4_url]) {
        [YCDownloadManager startDownloadWithUrl:model.mp4_url fileName:model.title thumbImageUrl:model.cover];
        }
        VideoCacheController *vc = [[VideoCacheController alloc] init];
        [self.navigationController pushViewController:vc animated:true];
        }
        demo    这里要加个判断不然有问题,不过不是大问题
        7b33a23272c4:@怀念裸奔的童年 会一直维护更新。互相请教。
      • glt_code:怎么设置最大下载数量?并且当设置好后,其他的完成后怎么可以继续下载等待的链接?
        glt_code:@LeavesLife 哈哈 厉害
        7b33a23272c4:目前只是简单的多任务下载,还没有细化完善。最近比较忙,快点明天会加上该功能,慢点下周一完善。

      本文标题:iOS 后台下载及管理库

      本文链接:https://www.haomeiwen.com/subject/ldpbsxtx.html