NSURLSession用法示例及断点续传视频的实现

                                

NSURLSession是苹果在WWDC2013中提出来的,旨在替代NSURLConnection,与咱们以前常用的NSURLConnection不一样,NSURLSession为咱们提供了更灵活的使用方法,包括后台下载以及断点续传的实现等功能.以前使用下载一直用的都是第三方框架好比OC的AFNetworking或者Swift的Alamofire.虽然第三方库用起来很方便也很稳定,可是仍是想本身研究下苹果原生的下载框架.这几天研究了下NSURLSession,作了一个加载视频的demo,包括了视频下载,断点续传的功能,本身简单总结下,也算是对本身学习过程的一个记录,嘿嘿.git

和使用NSURLConnection下载的时候建立NSURLConnection对象以前发起同步或者异步请求不一样,NSURLSession咱们使用的是它的三个子类,先简单说下NSURLSession的使用流程吧:github

  1. 建立 NSURLSessionConfiguration
  2. 使用 NSURLSessionConfiguration建立NSURLSession
  3. 使用NSURLSession来建立NSURLSessionTask对象
  4. 使用NSURLSessionTask对象来下载文件

具体介绍下这三个东西是用来干吗的:缓存

  • NSURLSessionConfiguration 
    NSURLSessionConfiguration是NSURLSession的配置文件,其中包括了缓存策略,请求超时时长以及使用什么网络类型请求等属性,其建立方法有三种: 
    +(NSURLSessionConfiguration *)defaultSessionConfiguration; //默认模式,可使用缓存的Cache,Cookie 
    +(NSURLSessionConfiguration *)ephemeralSessionConfiguration;//瞬时会话模式 不可使用缓存的Cache,Cookie,鉴权 
    +(NSURLSessionConfiguration*)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);//后台模式 能够在后台进行下载

NSURLSessionConfiguration有个重要的属性markdown

/* allow request to route over cellular. */ @property BOOL allowsCellularAccess; /* allows background tasks to be scheduled at the discretion of the system for optimal performance. */ @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(10_10, 7_0);

allowsCellularAccess属性为是否容许在移动网络下下载文件,通常状况下建议使用discretionary,该属性容许应用自动判断当前使用哪一种网络下载比较好.网络

  • NSURLSession 
    NSURLSession是用来生成和管理任务的,它有三种建立模式,以下所示
+ (NSURLSession *)sharedSession; 建立全局的session,返回共享的会话,使用全局的Cache、Cookie和证书 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; 使用本身建立的配置文件建立session + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue; 使用本身建立的配置文件建立session,而且能够设置他的代理,用来监听他的代理事件,从而监听下载进度以及将下载好的文件转移等操做,用起来十分方便,也是咱们最常常用的一种模式

重点是第三种建立方法,这也是最常用的方法session

+(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration 
delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

重点说一下其参数意义: 
1. configuration:咱们建立的配置文件,传进去就能够了 
2. delegate: 通常设置本身为其代理 
3. delegateQueue: 方法回调队列,这个须要咱们传一个NSOperationQueue队列进去,其代理回调函数都会在咱们传进去的队列执行.若是咱们传nil的话,其代理回调函数则会在添加到全局并发队列里去,将会开启多个线程来执行任务下载.并发

  • NSURLSessionTask 
    任务对象,也是咱们开启下载的对象,它有三个子类,咱们使用的时候也是直接使用它的子类
NSURLSessionDataTask
NSURLSessionUploadTask
NSURLSessionDownloadTask

做用不言而喻,上传时候使用NSURLSessionUploadTask,下载的时候使用NSURLSessionDownloadTask,至于NSURLSessionDataTask,咱们看下苹果的官方说明:app

/*
 * An NSURLSessionDataTask does not provide any additional * functionality over an NSURLSessionTask and its presence is merely * to provide lexical differentiation from download and upload tasks. */ @interface NSURLSessionDataTask : NSURLSessionTask

翻译:相比于NSURLSessionTask她不提额外的功能,它的存在仅仅是为了和download以及upload词汇方面的不一样. 呵呵哒框架

基本上,咱们上传和下载使用那两个类就足够了,至于这个为了提供词汇不一样的NSURLSessionDataTask,基本上用不到.iphone

以NSURLSessionDownloadTask为例,咱们看下他的建立方法:

/* Creates a download task with the given request. */ 使用咱们建立好的request来建立下载任务,和NSURLConnection使用方法相似 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; /* Creates a download task to download the contents of the given URL. */ 直接使用连接建立下载任务 - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; /* Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */ 使用续传数据来建立下载任务 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

建立好以后,开启任务的方法: 
[self.downloadTask resume]; //开始下载文件

  • 建立示例 
    说了这么多,来看下具体的使用示例吧,提供一个简单完整的建立流程和开启任务的方法,保证看完秒懂 0.0:
示例代码:
 //建立configuration NSURLSessionConfiguration *configuration ==[NSURLSessionConfiguration defaultSessionConfiguration]; configuration.discretionary = YES; //使用建立的configuration建立session 并将本身设置为代理 NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; //建立request k_DownloadURLStr是我定义的宏定义网址连接 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:k_DownloadURLStr]]; //使用建立的session和request建立NSURLSessionDownloadTask NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; //开启下载任务 [downloadTask resume]; [downloadTask invalidateAndCancel]; 关闭下载任务 [downloadTask finishTasksAndInvalidate]; 等待当前Task结束后关闭

既然设置本身为session的代理,须要实现的代理协议为NSURLSessionDownloadDelegate,通常使用NSURLSessionDownloadDelegate代理中的如下三个方法:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 当下载任务开启以后,一旦有数据返回,那么这个方法就会被调用,其参数意义以下: bytesWritten : 本次调用返回了多少字节的数据 totalBytesWritten: 一共返回了多少字节的数据 totalBytesExpectedToWrite: 该下载文件的大小 经过这个方法,咱们就能够实时的监听下载的进度了

当下载完成以后,会调用

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location

值得一提的是,当文件正在下载的时候,文件是下载到沙盒中的tmp文件夹中的,当下载完成以后,文件仍然存在于该文件夹中.可是该文件夹中的内容当iphone从新启动的时候就会被清空,因此咱们须要将下载好的文件转移到cache文件夹中保存起来,给出一个操做示例

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSURL *urlOfSave = [NSURL fileURLWithPath:paths[0]]; urlOfSave = [urlOfSave URLByAppendingPathComponent:@"冰河世纪.mov"]; NSLog(@"%@",urlOfSave); NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:urlOfSave.path]) { //若是文件夹下有同名文件 则将其删除 [fileManager removeItemAtURL:urlOfSave error:nil]; } //将下载好的文件复制到存储的文件夹下 [fileManager copyItemAtURL:location toURL:urlOfSave error:nil]; [self.session invalidateAndCancel]; self.session = nil; }

当咱们关闭任务或者手动结束任务的时候,会调用:

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
  • 断点续传的实现
其中resumeData中保存的并非当前下载的文件,将其拿到转出来的内容是xml报文,内容以下所示,(部分冗余的内容我已经切掉了),部分地方我已经添加注释.若是要实现断点续传功能的话,首先要调用方法
    __weak typeof (self)weakSelf = self; [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) { weakSelf.downloadData = resumeData; }]; downloadData是我建立的全局变量,用来保存中断的数据, 接着须要使用方法 self.downloadTask = [self.session downloadTaskWithResumeData:self.downloadData]; [self.downloadTask resume]; 来从新开启下载任务

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSURLSessionDownloadURL</key> <string>http://oarbi0614.bkt.clouddn.com/%E5%86%B0%E6%B2%B3%E4%B8%96%E7%BA%AA.mp4</string> 请求的地址 <key>NSURLSessionResumeBytesReceived</key> <integer>12038723</integer> 当前下载的文件大小 <key>NSURLSessionResumeCurrentRequest</key> <data> YnBsaXN0MDD..... </data> <key>NSURLSessionResumeEntityTag</key> <string>"ljECR82nRMhHvP8D5M9sGQuKBjgK"</string> <key>NSURLSessionResumeInfoTempFileName</key> <string>CFNetworkDownload_6XdKfZ.tmp</string> 下载使用的临时文件名 <key>NSURLSessionResumeInfoVersion</key> <integer>2</integer> <key>NSURLSessionResumeOriginalRequest</key> <data> YnBsaXN0MDD... </data> <key>NSURLSessionResumeServerDownloadDate</key> <string>Mon, 25 Jul 2016 10:42:23 GMT</string> </dict> </plist>

当咱们暂停以后拿到resumeData,再从新使用resumeData下载的时候,session会使用该数据块中的信息去查找临时文件,而后继续下载.这样的话,咱们也就实现了断点续传的功能.

    • 延伸 
      这个虽然实现了断点续传的功能,可是若是应用在运行的时候被杀死,那么下次下载的时候仍然是须要从新下载,上次下载的数据仍然会丢失,因此说,该断点续传仅仅是使用在程序正常运行过程当中的暂停而后能够接着下载,若是程序意外终结那么下载数据仍然会丢失.关于这个问题,我想了不少的解决方案,在下一篇博客,我会具体说明我是如何实现的,也欢迎你们随时指教.

    • 结语 
      上面的动图是我作的一个demo,包括了视频的下载,播放,暂停,恢复播放的功能.其使用的加载百分比视图是本身封装的一个小控件,视频播放使用的是AVPlayer.该demo放在了gitHub上面,地址:https://github.com/TheRuningAnt/TestSession.git 欢迎各位下载查看.喜欢的话记得给我一个星星哦.另外,该博文中有任何错误或不足之处,还请各位看官不吝赐教.多谢!

相关文章
相关标签/搜索