iOS边练边学--NSURLSessionDataTask实现文件真正的断点续传

实现重点:

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

如图:

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

代码如下:

  1 #import "ViewController.h"
  2 #import "NSString+Hash.h"
  3
  4 // 下载文件的URL
  5 #define ChaosFileURL @"http://120.25.226.186:32812/resources/videos/minion_01.mp4"
  6
  7 // 根据文件唯一的URL MD5值 作为文件名
  8 #define ChaosFileName ChaosFileURL.md5String
  9
 10 // 用来存储文件总长度的plist文件 key:文件名的MD5值 value:文件总长度
 11 #define ChaosDownloadFilesPlist [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"downloadFiles.plist"]
 12
 13 // 下载文件的全路径
 14 #define ChaosFileFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:ChaosFileName]
 15
 16 // 已经下载的文件长度
 17 #define ChaosDownloadLength [[[NSFileManager defaultManager] attributesOfItemAtPath:ChaosFileFullPath error:nil][@"NSFileSize"] integerValue]
 18
 19 @interface ViewController () <NSURLSessionDataDelegate>
 20
 21 /** stream */
 22 @property(nonatomic,strong) NSOutputStream *stream;
 23
 24 /** session */
 25 @property(nonatomic,strong) NSURLSession *session;
 26
 27 /** task */
 28 @property(nonatomic,strong) NSURLSessionDataTask *task;
 29
 30 /** totalLength */
 31 @property(nonatomic,assign) NSInteger totalLength;
 32
 33 /** downloadLength */
 34 @property(nonatomic,assign) NSInteger downloadLength;
 35
 36 @end
 37
 38 @implementation ViewController
 39
 40 - (NSURLSession *)session
 41 {
 42     if (_session == nil) {
 43
 44         _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
 45     }
 46
 47     return _session;
 48 }
 49
 50 - (NSURLSessionDataTask *)task
 51 {
 52     if (_task == nil) {
 53
 54         // 获得文件总长度
 55         NSInteger totalLength = [[NSDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist][ChaosFileName] integerValue];
 56         // 请求同一个文件,判断下载文件长度;如果没下载过此文件,totalLength = 0
 57         if (totalLength && ChaosDownloadLength == totalLength) {
 58             NSLog(@"文件已经下载过.");
 59             return nil;
 60         }
 61
 62         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ChaosFileURL]];
 63
 64         // 设置请求头 -- range 这次从哪里开始请求数据 格式:bytes=***-***(从指定开始到指定结束)  或者:bytes=***-(从指定位置到结束)
 65         NSString *range = [NSString stringWithFormat:@"bytes=%zd-",ChaosDownloadLength];
 66
 67         [request setValue:range forHTTPHeaderField:@"Range"];
 68
 69         _task = [self.session dataTaskWithRequest:request];
 70
 71     }
 72     return _task;
 73 }
 74
 75 // 开始
 76 - (IBAction)startClick:(id)sender {
 77
 78     [self.task resume];
 79 }
 80 // 暂停
 81 - (IBAction)pauseClick:(id)sender {
 82
 83     [self.task suspend];
 84 }
 85
 86 - (void)viewDidLoad {
 87     [super viewDidLoad];
 88 }
 89
 90 #pragma mark - <NSURLSessionDataDelegate>
 91 /**
 92  * 接收到响应的时候调用
 93  */
 94 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
 95 {
 96     // 调用blcok,才能接受到数据
 97     completionHandler(NSURLSessionResponseAllow);
 98     // 初始化stream
 99     self.stream = [NSOutputStream outputStreamToFileAtPath:ChaosFileFullPath append:YES];
100     [self.stream open];
101
102     // 获取文件总长度
103     self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + ChaosDownloadLength;
104
105     // 接收到服务器响应的时候存储文件的总长度到plist,实现多文件下载,先取出字典,给字典赋值最后写入。
106     // 错误做法:直接写入文件,会用这次写入的信息覆盖原来所有的信息
107     NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist];
108     // 字典可能为空
109     if (dict == nil) dict = [NSMutableDictionary dictionary];
110     // 写入文件
111     dict[ChaosFileName] = @(self.totalLength);
112     [dict writeToFile:ChaosDownloadFilesPlist atomically:YES];
113 }
114
115 /**
116  * 接收到服务器发来的数据的时候调用 -- 有可能调用多次
117  */
118 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
119 {
120     // 写入数据
121     [self.stream write:[data bytes] maxLength:data.length];
122     // 获取已经下载的长度
123     self.downloadLength = ChaosDownloadLength;
124     // 计算进度
125     NSLog(@"%f",1.0 * self.downloadLength / self.totalLength);
126 }
127
128 /**
129  * 任务完成的时候调用
130  */
131 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
132 {
133     NSLog(@"----finish");
134     [self.stream close];
135     self.stream = nil;
136
137     // 一个任务对应一个文件,用完清空
138     self.task = nil;
139 }
140 @end
时间: 2024-12-14 23:36:05

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

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

一.Xib和storyboard对比 *共同点: 1>都用来描述软件界面 2>都用Interface Builder工具来编辑 3>本质都是转换成代码去创建控件 *不同点 1>Xib是轻量级的,用来描述局部的UI界面 2>storyboard是重量级的,用来描述整个软件的多个界面,并且能展示多个界面之间的跳转关系 二.Xib的加载方法 方法1: NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"xib文件名

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

一.非cocoaPods方法,需要注意的是:直接将SSZipArchive拖入项目编译会报错. Undefined symbols for architecture x86_64: "_crc32", referenced from: _unzReadCurrentFile in unzip.o _zipWriteInFileInZip in zip.o "_deflate", referenced from: _zipWriteInFileInZip in zip

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

一.NSURLSession.NSURLSessionTask的使用步骤 首先创建NSURLSession对象 通过NSURLSession对象创建对应的任务 <1>NSURLSessionDataTask的GET和POST  -- 以及url中包含了中文的解决办法 <2>NSURLSessionDownloadTask实现小文件的下载 <3>NSURLSessionDownloadTask实现大文件的断点下载 -- 暂时没有实现退出程序后的文件续传 1 #import

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

iOS应用数据存储的常用方式: XML属性列表(plist)归档 Preference(偏好设置) NSKeyedArchiver归档(NSCoding) SQLite3--这里暂且不讲 Core Data--这里暂且不讲 一.应用沙盒 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离.应用必须待在自己的沙盒里,其他应用不能访问该沙盒 应用沙盒的文件系统目录,如下图所示 二.应用沙盒结构分析 应用程序包:(上图中的Layer)包含了所有的资源文件和可执行文件 Do

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

一.iOS中的多线程 多线程的原理(之前多线程这块没好好学,之前对多线程的理解也是错误的,这里更正,好好学习这块) iOS中多线程的实现方案有以下几种 二.NSThread线程类的简单实用(直接上代码) 三.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题(存钱取钱的例子,多个售票员卖票的例子) 安全隐患解决的方法 --- 互斥锁(图解)

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

一.AFNETWorking简单使用 get请求 get请求,以后经常用NSURLSession底层的写的部分 简单的post请求 用post请求下载文件,方法很多,还可以通过upload任务来执行 download任务 二.框架中的数据解析,默认是将数据按照json来解析,设置方法 三.AFN框架监控联网状态

iOS边练边学--CALayer,非根层隐式动画,钟表练习

一.CALayer UIView之所以能显示在屏幕上,完全是因为他内部的一个图层 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层 当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘制,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示 UIView本身不具备显示的功能,是他内部的层才有显示功能 二.CALayer的基本使用 三.关于CALa

iOS边练边学--触摸事件以及能够拖拽的UIView的练习

一.用户在使用APP的过程中,会产生各种各样的事件.iOS中的事件可以分为3大类型: 二.响应者对象 在iOS中只有继承了了UIResponder的对象才能接受并处理事件,这样的对象称之为“响应者对象” UIApplication.UIViewController.UIView都继承自UIResponder,因此他们都是响应者对象,都能够接受并处理事件 UIResponder内部提供了以下方法来处理事件 三.练习中对UIView的触摸事件进行了熟悉 四.UITouch 一根手指对应一个UITou

iOS边练边学--多线程练习的多图片下载 以及 使用第三方框架(SDWebImage)的多图片下载

一.自己实现多图片下载应该注意的问题 沙盒缓存的问题 程序缓存的问题 cell重复利用显示图片混乱的问题 -- 用户拖拽快,下载图片慢导致的 解决图片混乱引入NSOperation集合的问题 资源下载失败的问题(练习中没有出现过,但是一定要考虑) 1 #import "ChaosViewController.h" 2 #import "ChaosApp.h" 3 4 @interface ChaosViewController () 5 /** 模型集合 */ 6