1.断点续传
定义:我们有时在边聊天边下载大文件时,感觉很卡,这时可以暂停下载任务,聊完天再继续下载文件。这就是断点下载
实现方式:iOS中利用NSURLSession可以进行文件断点下载,下面就一起来实现它吧。
知识储备:1.对NSURLSesiion的认识
NSURLSesiion是苹果在iOS7推出的一个类,它具备了NSURLConnection所具备的方法,同时也比它更强大。苹果推出它的目的大有取代NSURLConnection的趋势或者目的。
2.NSURLSesiion的作用
实现对文件的下载与上传。在NSURLSesiion中,任何请求都可以被看做是一个任务。而NSURLSesiionData有两个子类:NSURLSessionDownlaodTask实现文件下载和NSURLSessionUploadTask实现文件上传。
3.NSURLSession的获取
NSURLSession的获取可以用NSURLSessionDownloadTaskdelegate的方法获取,但是必须得遵循这个协议。获取如下:
NSURLSessionDonfiguration*configuration=[NSURLSessionDonfiguration defaultSessionconfiguration];
self.session=[NsURLSessionsessionWithConfiguration:NSURLSessionDonfiguration delegate: self delegateQueue:[NSOperationQueue mainQueue]];
4.下载任务的创建
NSURLSessionDownlaodTask* task=[self.sessiondownloadTaskWithURL:url]
5.NSURLSessionDownloadDelegate的代理方法:
它有三个方法:
6.沙盒路径的获取
NSString*path=[NSSearchPathForDiretoriesInDomains(NSDocumentDirectoriy,NSUserDomainMask, YES ) lastObject];
7.caches路径的获取及里面文件名的创建
NSString *caches=[NSSearchPathForDiretoriesInDomains(NSCachesDirectoriy,NSUserDomainMask, YES ) lastObject];
NSString* filename=[cachesstringByAppendingPathComponent:downloadTask.response.suggestedname];
第二部分对程序几个属性的说明
1.resumeData
该参数包含了继续下载文件的位置信息。也就是说,当你下载了10M得文件数据,暂停了。那么你下次继续下载的时候是从第10M这个位置开始的,而不是从文件最开始的位置开始下载。因而为了保存这些信息,所以才定义了这个NSData类型的这个属性:resumeData
2. task
该参数的类型是NSURLSessionDownloadTask。因为在程序调用暂停(pause)这个方法时,必须拥有这个属性,怎么拿到它了?最好的办法就是让控制器拥有这个属性。
3. session
该参数的类型是NSURLSession.在程序调用继续下载(resume)这个方法时,必须拥有这个session。因为之前的任务task被取消了,无法在复用了,所以用懒加载的方法,让session只创建一次,同时也让控制器拥有了这个属性。
*使用NSURLSessionDataTask可以很轻松实现断点续传,可是有个致命的缺点就是无法进行后台下载
二:对NSURLSessionDownloadTask解释
使用NSURLSessionDataTask可以很轻松实现断点续传,可是有个致命的缺点就是无法进行后台下载,一点应用程序进入了后台,便会停止下载。所以无法满足我们的需求。而NSURLSessionDownloadTask是唯一可以实现后台下载的类,所以我们只能从这个类进行下手了。
那么本篇我们就来谈谈关于应用程序随时可能被杀死的情况下,如何进行断点续传。
(一):关于断点续传原理:
首先,如果想要进行断点续传,那么需要简单了解一下断点续传的工作机制,在HTTP请求头中,有一个Range的关键字,通过这个关键字可以告诉服务器返回哪些数据给我。
比如:
bytes=500-999 表示第500-第999字节
bytes=500- 表示从第500字节往后的所有字节
然后我们再根据服务器返回的数据,将得到的data数据拼接到文件后面,就可以实现断点续传了。
(二):关于文件下载与暂停的分析:
1.当使用NSURLSessionDownloadTask进行下载的时候,系统会在cache文件夹下创建一个下载的路径.(路径下会有一个以"CFNetworking"打头的.tmp文件(以下简称"下载文件"防止混淆),这个就是我们正在下载中的文件)
2.调用cancelByProducingResumeData:方法后,会得到一个data文件,通过String格式化后,发现是一个XML文件.
3.XML里面包含了关于.tmp文件的一些关键点的描述,包括"Range","key","下载文件的路径"等等.而原本存在于download文件下的下载文件,则被移动到了系统tmp文件夹目录下.
4.当我们再次进行resume操作的时候,下载文件则又被移回到了download文件夹下。
(三):关于程序被杀掉的断点续传resumeData
根据上面的分析,基本可以得到以下结论:
1.DownloadTask每次进行断点续传的时候,会根据data文件中的"路径Key"去寻找下载文件,然后校验后再根据"Range"属性去进行断点续传。
2.download文件夹中存放的只会是下载中的文件,一旦暂停就会被移动到tmp文件夹下。
3.每个暂停得到的data文件,与下载文件一一对应。
3.断点续传只与tmp文件夹中的文件有关。
具体实现
为了节省性能,我尝试查找关于程序被杀掉前的回调,但是很遗憾失败了,因为我无法控制到知道是哪一秒去保存进度,所以我只能每隔一段时间保存一次。设置一个Bool变量用来判断是否正在下载中,同时用一个周期事件每隔一段时间暂停一次(听上去挺笨的,但是这似乎是唯一获得data文件的办法了)。然后保存data文件和拷贝tmp文件夹下的下载文件到安全目录下(因为tmp文件夹据说随时可能清空)。
当再次下载的时候,先是从安全目录下取到下载文件,删除tmp文件夹中原有的同名文件,然后copy到tmp目录下,最后利用保存的data文件进行再次downloadTaskWithResumeData操作,就可以实现再次下载了。
利与弊
好处:
1.DownloadTask可以后台下载,不必保持app在前台,用户体验很好。
2.实现了任意时间点杀掉进程后,仍然可以断点续传。
缺陷:
1.因为苹果没有提供很好的API,所以会有一个循环检查,每隔一段时间会暂停个一秒左右,效率略有降低。
2.如果设置保存间隔过长,中间杀掉进程可能会损失较多进度。