总结iOS开发中的断点续传那些事儿

前言

断点续传概述

断点续传就是从文件赏赐中断的地方从新开始下载或者上传数据,而不是从头文件开始。当下载大文件的时候,若是没有实现断点续传功能,那么每次出现异常或者用户主动的暂停,都会从头下载,这样很浪费时间有木有。因此呢,项目中实现大文件下载的时候,断点续传功能是必不可少了。固然咯,断点续传有一种特殊的状况,就是咱们的应用呗用户kill掉或者应用crash,要实现应用重启以后的断点续传,这种状况就是咱们将要解决的问题。html

断点续传的原理

要实现断点续传,服务器必须是要支持的。目前最多见的两种方式:FTPHTTP安全

下面来简单介绍HTTP断点续传的原理。服务器

HTTP

经过HTTP,能够很是方便的实现断点续传。断点续传主要依赖于HTTP头部定义的Range,应用能够经过HTTP请求曾经获取失败的资源的某一个返回或者部分来恢复下载该资源。固然并非全部风服务器都支持Range,因此不支持Range的不在咱们考虑以内。Range是以字节计算的,请求的时候不比给我结尾字节数,由于请求方并不必定知道资源的大小。
经过这个关键字能够告诉服务器返回哪些数据给我。
好比:
bytes=500-999 表示第500-第999字节
bytes=500- 表示从第500字节日后的全部字节
而后咱们再根据服务器返回的数据,将获得的data数据拼接到文件后面,就能够实现断点续传了。session

断点续传分析—AFHTTPRequestOperation

在了解了断点续传的原理以后,咱们就能够动手来实现iOS中的断点续传了。因为我如今接触到的项目都是部署在HTTP服务器上的,因此断点续传功能也基于HTTP实现。首先咱们来最简单的入手,第三方神奇AFNetworking中提供的实现,下面请看详细代码:app

//1.指定下载文件的地址URLString
 //2.获取保存的文件路径filePath
 //3.得到NSURLRquest
    NSString* URLString = @"";
    NSString* filePath = @"";

    NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]];
    signed long long downloadBytes = 0;
    ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        // 3.1 若以前下载过 , 则在 HTTP 请求头部加入 Range
        // 获取已下载文件的 size
        downloadedBytes = [self fileSizeForPath:filePath];

        // 验证是否下载过文件
        if (downloadedBytes > 0) {
            // 若下载过 , 断点续传的时候修改 HTTP 头部部分的 Range
            NSMutableURLRequest *mutableURLRequest = [request mutableCopy];
            NSString *requestRange =
            [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes];
            [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"];
            request = mutableURLRequest;
        }
    }

    // 4 建立 AFHTTPRequestOperation
    AFHTTPRequestOperation *operation
    = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    // 5 设置操做输出流 , 保存在第 2 步的文件中
    operation.outputStream = [NSOutputStream
                              outputStreamToFileAtPath:filePath append:YES];

    // 6 设置下载进度处理 block
    [operation setDownloadProgressBlock:^(NSUInteger bytesRead,
                                          long long totalBytesRead, long long totalBytesExpectedToRead) {
        // bytesRead 当前读取的字节数
        // totalBytesRead 读取的总字节数 , 包含断点续传以前的
        // totalBytesExpectedToRead 文件总大小
    }]; 

    // 7 设置 success 和 failure 处理 block 
    [operation setCompletionBlockWithSuccess:^(
                                               AFHTTPRequestOperation *operation,
                                               id responseObject) {


    }                                failure:^(AFHTTPRequestOperation *operation,
                                               NSError *error) {

    }];

    // 8 启动 operation
    [operation start];

使用以上代码 , 断点续传功能就实现了,应用从新启动或者出现异常状况下 , 均可以基于已经下载的部分开始继续下载。关键的地方就是把已经下载的数据持久化。接下来简单看下AFHTTPRequestOperation是怎么实现的。经过查看源码 , 咱们发现 AFHTTPRequestOperation 继承自 AFURLConnectionOperation,而AFURLConnectionOperation 实现了 NSURLConnectionDataDelegate 协议。异步

处理流程如图所示:

能够看到,这里的AFNetworking采起自线程调一步接口的方式,是由于直接在主线程调用异步接口会有一个Runloop的问题。当主线程调用[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]时,请求发出以后的监放任务会加到主线程中的Runloop中,咱们知道RunloopMode默认为NSDefuaultRunloopMode,这个表示只有当前线程的Runloop处理NSDefaultRunloopMode时,这个任务才会执行。而当用户正在滚动tableview和scrollview的时候,主线程的Runloop处于NSEventTrackingRunloop模式下,就不会执行NSDefaultRunloopMode的任务。oop

另外因为采起子线程调用接口的方式,因此这边的DownloadProgressBlock,success 和 failure Block 都须要回到主线程来处理。线程

断点续传实战

NSURLConnecttion的实现

NSURLConnecttion这家伙已经在2015年就已经被苹果遗弃,因此在这里咱们不作过多讨论,请注意啊,我是省略号……code

NSURLSessionDataTask

苹果在iOS7开始,推出一个新的类NSURLSession,它具有了NSURLConnection所具有的方法,而且更增强大。因此我更加推荐你们使用这个类去实现下载和续传。NSURLConnection 和 NSURLSession delegate 方法的映射关系 , 以下图所示。因此关键是要知足 NSURLSessionDataDelegate 和 NSURLsessionTaskDelegate。orm

文件下载和暂停的实现

当使用NSURLSessionDownloadTask进行下载的时候,系统会在cache文件夹下建立一个下载的路径,路径下会有一个以”CFNetworking”打头的.tmp文件(如下简称”下载文件”防止混淆),这个就是咱们正在下载中的文件。而当咱们调用了cancelByProducingResumeData:方法后,会获得一个data文件,经过String格式化后,发现是一个XML文件,里面包含了关于.tmp文件的一些关键点的描述,包括”Range”,”key”,”下载文件的路径”等等.而本来存在于download文件下的下载文件,则被移动到了系统tmp文件夹目录下.而当咱们再次进行resume操做的时候,下载文件则又被移回到了download文件夹下。

程序被杀掉的断点续传resumeData

根据上面的分析,基本能够获得如下结论:

  • DownloadTask每次进行断点续传的时候,会根据data文件中的”路径Key”去寻找下载文件,而后校验后再根据”Range”属性去进行断点续传。

  • download文件夹中存放的只会是下载中的文件,一旦暂停就会被移动到tmp文件夹下。

  • 每一个暂停获得的data文件,与下载文件一一对应。

  • 断点续传只与tmp文件夹中的文件有关。

因此咱们能够这么作,设置一个Bool变量用来判断是否正在下载中,同时用一个周期事件每隔一段时间暂停一次。而后保存data文件和拷贝tmp文件夹下的下载文件到安全目录下(由于tmp文件夹听说随时可能清空)。
当再次下载的时候,先是从安全目录下取到下载文件,删除tmp文件夹中原有的同名文件,而后copy到tmp目录下,最后利用保存的data文件进行再次downloadTaskWithResumeData操做,就能够实现再次下载了。

原文连接:总结iOS开发中的断点续传那些事儿

相关文章
相关标签/搜索