实现重点:

  • NSURLSessionDataTask要设置请求头,从路径中获取文件已经下载的长度(文件没有下载过的话,长度为0)。通过这个长度设置请求的Range

如图:

  • 接收到请求的时候key:文件名(经过MD5加密过的URL,Url保证了文件名的唯一) Value:该文件已经下载过的长度。保存成plist文件,方便对下载文件的判断
  • 利用NSOutUpStream写文件
  • 在任务完成的代理方法里面,NSOutUpStream关闭并且清空,对应的task清空,对应的session清空

代码如下:

 #import "ViewController.h"
#import "NSString+Hash.h" // 下载文件的URL
#define ChaosFileURL @"http://120.25.226.186:32812/resources/videos/minion_01.mp4" // 根据文件唯一的URL MD5值 作为文件名
#define ChaosFileName ChaosFileURL.md5String // 用来存储文件总长度的plist文件 key:文件名的MD5值 value:文件总长度
#define ChaosDownloadFilesPlist [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"downloadFiles.plist"] // 下载文件的全路径
#define ChaosFileFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:ChaosFileName] // 已经下载的文件长度
#define ChaosDownloadLength [[[NSFileManager defaultManager] attributesOfItemAtPath:ChaosFileFullPath error:nil][@"NSFileSize"] integerValue] @interface ViewController () <NSURLSessionDataDelegate> /** stream */
@property(nonatomic,strong) NSOutputStream *stream; /** session */
@property(nonatomic,strong) NSURLSession *session; /** task */
@property(nonatomic,strong) NSURLSessionDataTask *task; /** totalLength */
@property(nonatomic,assign) NSInteger totalLength; /** downloadLength */
@property(nonatomic,assign) NSInteger downloadLength; @end @implementation ViewController - (NSURLSession *)session
{
if (_session == nil) { _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
} return _session;
} - (NSURLSessionDataTask *)task
{
if (_task == nil) { // 获得文件总长度
NSInteger totalLength = [[NSDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist][ChaosFileName] integerValue];
// 请求同一个文件,判断下载文件长度;如果没下载过此文件,totalLength = 0
if (totalLength && ChaosDownloadLength == totalLength) {
NSLog(@"文件已经下载过.");
return nil;
} NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ChaosFileURL]]; // 设置请求头 -- range 这次从哪里开始请求数据 格式:bytes=***-***(从指定开始到指定结束) 或者:bytes=***-(从指定位置到结束)
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",ChaosDownloadLength]; [request setValue:range forHTTPHeaderField:@"Range"]; _task = [self.session dataTaskWithRequest:request]; }
return _task;
} // 开始
- (IBAction)startClick:(id)sender { [self.task resume];
}
// 暂停
- (IBAction)pauseClick:(id)sender { [self.task suspend];
} - (void)viewDidLoad {
[super viewDidLoad];
} #pragma mark - <NSURLSessionDataDelegate>
/**
* 接收到响应的时候调用
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
// 调用blcok,才能接受到数据
completionHandler(NSURLSessionResponseAllow);
// 初始化stream
self.stream = [NSOutputStream outputStreamToFileAtPath:ChaosFileFullPath append:YES];
[self.stream open]; // 获取文件总长度
self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + ChaosDownloadLength; // 接收到服务器响应的时候存储文件的总长度到plist,实现多文件下载,先取出字典,给字典赋值最后写入。
// 错误做法:直接写入文件,会用这次写入的信息覆盖原来所有的信息
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist];
// 字典可能为空
if (dict == nil) dict = [NSMutableDictionary dictionary];
// 写入文件
dict[ChaosFileName] = @(self.totalLength);
[dict writeToFile:ChaosDownloadFilesPlist atomically:YES];
} /**
* 接收到服务器发来的数据的时候调用 -- 有可能调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// 写入数据
[self.stream write:[data bytes] maxLength:data.length];
// 获取已经下载的长度
self.downloadLength = ChaosDownloadLength;
// 计算进度
NSLog(@"%f",1.0 * self.downloadLength / self.totalLength);
} /**
* 任务完成的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"----finish");
[self.stream close];
self.stream = nil; // 一个任务对应一个文件,用完清空
self.task = nil;
}
@end

iOS边练边学--NSURLSessionDataTask实现文件真正的断点续传的更多相关文章

  1. iOS边练边学--UIScrollView和xib文件实现简单分页+定时器初使用

    一.xib文件构成 二.自定义控件类(xib文件与自定义控件类的文件名字相同,并且将xib文件中父类控件的类名改成自定义控件类的名称) ***********自定义控件类需要的属性********** ...

  2. iOS边练边学--xib文件初使用

    一.Xib和storyboard对比 *共同点: 1>都用来描述软件界面 2>都用Interface Builder工具来编辑 3>本质都是转换成代码去创建控件 *不同点 1> ...

  3. iOS边练边学--plist文件,懒加载,模型初使用--补充instancetype

    一.什么是plist文件 1>将数据直接写在代码里面,不是一种合理的做法.如果数据经常修改,就要经常翻开对应的代码进行修改,造成代码扩展性低 2>因此,可以考虑将经常变得数据放在文件中进行 ...

  4. iOS边练边学--文件压缩和解压缩的第三方框架SSZipArchive的简单使用

    一.非cocoaPods方法,需要注意的是:直接将SSZipArchive拖入项目编译会报错. Undefined symbols for architecture x86_64: "_cr ...

  5. iOS边练边学--NSURLSession、NSURLSessionTask的介绍与使用以及url中包含了中文的处理方法

    一.NSURLSession.NSURLSessionTask的使用步骤 首先创建NSURLSession对象 通过NSURLSession对象创建对应的任务 <1>NSURLSessio ...

  6. iOS边练边学--多线程介绍、NSThread的简单实用、线程安全以及线程之间的通信

    一.iOS中的多线程 多线程的原理(之前多线程这块没好好学,之前对多线程的理解也是错误的,这里更正,好好学习这块) iOS中多线程的实现方案有以下几种 二.NSThread线程类的简单实用(直接上代码 ...

  7. iOS边练边学--应用数据存储的常用方式(plist,Preference,NSKeyedArchiver)其中的三种

    iOS应用数据存储的常用方式: XML属性列表(plist)归档 Preference(偏好设置) NSKeyedArchiver归档(NSCoding) SQLite3--这里暂且不讲 Core D ...

  8. iOS边练边学--AFNetWorking框架GET、Post、Download、Upload,数据解析模式以及监控联网状态

    一.AFNETWorking简单使用 get请求 get请求,以后经常用NSURLSession底层的写的部分 简单的post请求 用post请求下载文件,方法很多,还可以通过upload任务来执行 ...

  9. iOS边练边学--iOS中的XML数据解析

    XML的解析方式 SAX 大小文件都可以 NSXMLParser DOM 最好是小文件 GDataXML NSXMLParser的用法 创建解析器来解析 // 创建XML解析器 NSXMLParser ...

随机推荐

  1. 分享用于学习C++图像处理的代码示例

    为了便于学习图像处理并研究图像算法, 俺写了一个适合初学者学习的小小框架. 麻雀虽小五脏俱全. 采用的加解码库:stb_image 官方:http://nothings.org/ stb_image. ...

  2. 定制你的Unity编辑器

    Unity的编辑器可以通过写脚本进行界面定制,包括添加功能菜单,今天写游戏Demo用到了记录一下. 为Unity添加子菜单 示例程序 [AddComponentMenu("Defend Ho ...

  3. [cb] Unity Editor 添加右键菜单

    需求 为Unity的Editor窗口添加右键菜单 实现代码 // This example shows how to create a context menu inside a custom Edi ...

  4. Volley(六 )—— 从源码带看Volley的缓存机制

    磁盘缓存DiskBasedCache 如果你还不知道volley有磁盘缓存的话,请看一下我的另一篇博客请注意,Volley已默认使用磁盘缓存 DiskBasedCache内部结构 它由两部分组成,一部 ...

  5. RDLC系列之七 条码打印

    参考: C# 条码标签打印程序,RDLC报表动态显示多条码标签的方法 http://www.cnblogs.com/vice/p/4105898.html 我做的思路是:不使用数据库存储image的b ...

  6. [转]有关WorldWind1.4的worldwind.cs窗口设计器打开错误的解决方法

    Solution for Designer error when opening WorldWind.cs in WW1.4.0 When I load the WW project in my Vi ...

  7. [转]C#使用Log4Net记录日志

    第一步:下载Log4Net 下载地址:http://logging.apache.org/log4net/download_log4net.cgi 把下载的  log4net-1.2.11-bin-n ...

  8. 在页面上以消息束的形式同时抛出多个DialogMessage

    com.sun.java.util.collections.ArrayList exceptions = new com.sun.java.util.collections.ArrayList(); ...

  9. Caffe学习系列(17):模型各层数据和参数可视化

    cifar10的各层数据和参数可视化 .caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1p ...

  10. 关于多个EditText的OnTextChange事件陷入死循环的处理

    需求:ListView的Item上面有三个EditText控件,分别为 数量 ,单价,总价,要求输入数量跟单价时候 总价跟着计算变化,当输入总价时候 数量不变,改变单价. 实现:首先肯定想到的是对Ed ...