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

时间:2022-01-22 10:44:35

前言

断点续传概述

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

断点续传的原理

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

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

HTTP

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

断点续传分析—AFHTTPRequestOperation

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

 //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的任务。

另外由于采取子线程调用接口的方式,所以这边的DownloadProgressBlock,success 和 failure Block 都需要回到主线程来处理。

断点续传实战

NSURLConnecttion的实现

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

NSURLSessionDataTask

苹果在iOS7开始,推出一个新的类NSURLSession,它具备了NSURLConnection所具备的方法,并且更加强大。所以我更加推荐大家使用这个类去实现下载和续传。NSURLConnection 和 NSURLSession delegate 方法的映射关系 , 如下图所示。所以关键是要满足 NSURLSessionDataDelegate 和 NSURLsessionTaskDelegate。

文件下载和暂停的实现

当使用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开发中的断点续传那些事儿

总结iOS开发中的断点续传那些事儿的更多相关文章

  1. iOS 应用开发中的断点续传实践总结

    断点续传概述 断点续传就是从文件上次中断的地方开始重新下载或上传数据,而不是从文件开头.(本文的断点续传仅涉及下载,上传不在讨论之内)当下载大文件的时候,如果没有实现断点续传功能,那么每次出现异常或者 ...

  2. iOS开发中关于UIImage的知识点总结

    UIImage是iOS中层级比较高的一个用来加载和绘制图像的一个类,更底层的类还有 CGImage,以及iOS5.0以后新增加的CIImage.今天我们主要聊一聊UIImage的三个属性: image ...

  3. iOS开发中静态库之".framework静态库"的制作及使用篇

    iOS开发中静态库之".framework静态库"的制作及使用篇 .framework静态库支持OC和swift .a静态库如何制作可参照上一篇: iOS开发中静态库之" ...

  4. iOS开发中静态库制作 之.a静态库制作及使用篇

    iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...

  5. ios开发中的小技巧

    在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIViewal ...

  6. IOS 开发中 Whose view is not in the window hierarchy 错误的解决办法

    在 IOS 开发当中经常碰到 whose view is not in the window hierarchy 的错误,该错误简单的说,是由于 "ViewController" ...

  7. [转]iOS开发中的火星坐标系及各种坐标系转换算法

     iOS开发中的火星坐标系及各种坐标系转换算法 源:https://my.oschina.net/u/2607703/blog/619183   其原理是这样的:保密局开发了一个系统,能将实际的坐标转 ...

  8. iOS开发中常见问题集锦

    在iOS开发中,会出现各种各样的问题.今天,就把这些常见的问题以及各位大牛的解决方案汇总下,方便以后查阅: 常见错误: 1. linker command failed with exit code ...

  9. iOS开发中获取WiFi相关信息

    iOS 开发中难免会遇到很多与网络方面的判断,这里做个汇总,大多可能是与WiFi相关的. 1.Ping域名.Ping某IP 有 时候可能会遇到ping 某个域名或者ip通不通,再做下一步操作.这里的p ...

随机推荐

  1. js 数组 转

    1.数组的创建 var arrayObj = new Array(); //创建一个数组 var arrayObj = new Array([size]); //创建一个数组并指定长度,注意不是上限, ...

  2. CentOS 安装中文输入法

    转载:http://blog.sina.com.cn/s/blog_9f1c093101019h03.html centos 6.3用yum安装中文输入法 1.需要root权限,所以要用root登录 ...

  3. contentProvider模板

    package com.example.qunzheng.todolist.provider; import android.content.ContentProvider; import andro ...

  4. TP-LINK wr703n openwrt 挂载 U盘

    1.首先设置好DNS 2.点SYSTEM 点SOFTWARE 更新软件列表 3.安装下列软件: block-mount kmod-usb-storage kmod-fs-ext4 e2fsprogs ...

  5. C++封装SQLite实例<三>

    前一篇博客中介绍的是怎样依据sqlite3_get_table()函数来获取一张表的内容,就是一股脑的把表中的内容所有存储起来放在一个一维数组中,这其中的规则已经介绍过了.接下来讲的是怎样依据一个SQ ...

  6. LoadRuner12.53教程(二)

    使用HP Web访问示例应用程序 shǐ使   yòng用   H   P   W   e   b   fǎng访   wèn问   shì示   lì例   yìng应   yòng用   chén ...

  7. 树莓派无显示器开启ssh的方法

    在boot根目录新建一个名为 ssh 的空文件即可. boot目录所在分区是fat32格式,可以被windows识别和操作 带有系统的tf卡(或SD卡)插入读卡器中,新建ssh文件即可,注意无后缀名

  8. 使用外网访问阿里云服务器ZooKeeper

    参考网址: zookeeper单机/集群安装详解 使用外网访问阿里云服务器ZooKeeper 阿里云服务管理控制台 1. 阿里云ECS安装zookeeper 环境:我安装的是zookeeper3.4. ...

  9. artTemplate 简洁语法版

    引用简洁语法的引擎版本,例如: <script src="dist/template.js"></script> 下载 表达式 {{ 与 }} 符号包裹起来 ...

  10. Google Protocol Buffers介绍

    简要介绍和总结protobuf的一些关键点,从我之前做的ppt里摘录而成,希望能节省protobuf初学者的入门时间.这是一个简单的Demo. Protobuf 简介 Protobuf全称Google ...