iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>
2016-02-19 14:18
1051 查看
前言:根据前篇《iOS开发之网络编程--2、NSURLSessionDownloadTask文件下载》或者《iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)》,都遗留了一个细节未处理的问题,那就是在离线断点下载的过程中,当应用程序重新启动之后,进度条的进度值默认没有设置为之前已经下载的进度,根据基本公式"当前进度值 = 已经下载的数据长度 ÷ 最终下载完的数据总长度",已经下载的数据长度可以由沙盒中已经下载的那部分数据获取,但是最终下载完的数据总长度就需要通过网络返回的信息了,但是别忘了,每一次重新启动应用程序初始状态默认都是暂停下载,或者是断网的情况下无法请求网络数据,那么如何获取这个"最终下载完的数据总长度"呢?
本篇还涉及到在子线程创建下载任务,然后通过线程通知UI主线程更新进度条控件显示进度。因为delegateQueue这个属性可以设置主队列线程或者是子队列线程。
先看看效果:
问题解决:"最终下载完的数据总长度"可以在首次从0开始下载的时候通过网络获取,然后将其"最终下载完的数据总长度"这个值存储在缓存中的某个文件(这个文件可以是字典等等)中,等待下一次获取。
而我则采用的方法是将这个"最终下载完的数据总长度"作为文件的属性添加进文件属性列表中,以备下一次读取的时候,获得到这个文件之后,就可以读取该文件的属性列表中的"最终下载完
的数据总长度"的属性和属性值。
在这里不得不先介绍一个工具类,读者可以通过本人的另一篇博文随笔先了解其功能:iOS开发 -- 为本地文件添加自定义属性的工具类
为本地文件添加属性之后,可以打印看的到:
本人花了点时间将网络下载这部分简单的封装成了一个工具类:DownloadTool,下面就展示源码:
DownloadTool.h
DownloadTool.m
使用示例源码:
效果图(中间有个过程是重新启动应用程序看看进度条显示的效果,然后继续测试开始下载和暂停下载):
百度云分享源码链接: http://pan.baidu.com/s/1eRwRkZo 密码: 787n
转载请注明出处:/article/5230898.html ,尊重劳动成果。
本篇还涉及到在子线程创建下载任务,然后通过线程通知UI主线程更新进度条控件显示进度。因为delegateQueue这个属性可以设置主队列线程或者是子队列线程。
先看看效果:
问题解决:"最终下载完的数据总长度"可以在首次从0开始下载的时候通过网络获取,然后将其"最终下载完的数据总长度"这个值存储在缓存中的某个文件(这个文件可以是字典等等)中,等待下一次获取。
而我则采用的方法是将这个"最终下载完的数据总长度"作为文件的属性添加进文件属性列表中,以备下一次读取的时候,获得到这个文件之后,就可以读取该文件的属性列表中的"最终下载完
的数据总长度"的属性和属性值。
在这里不得不先介绍一个工具类,读者可以通过本人的另一篇博文随笔先了解其功能:iOS开发 -- 为本地文件添加自定义属性的工具类
为本地文件添加属性之后,可以打印看的到:
本人花了点时间将网络下载这部分简单的封装成了一个工具类:DownloadTool,下面就展示源码:
DownloadTool.h
#import <Foundation/Foundation.h> // 定义一个block用来传递进度值 typedef void (^SetProgressValue)(float progressValue); @interface DownloadTool : NSObject /** 创建下载工具对象 */ + (instancetype)DownloadWithURLString:(NSString*)urlString setProgressValue:(SetProgressValue)setProgressValue; /** 开始下载 */ -(void)startDownload; /** 暂停下载 */ -(void)suspendDownload; @end
DownloadTool.m
#import "DownloadTool.h" #import "ExpendFileAttributes.h" #define Key_FileTotalSize @"Key_FileTotalSize" @interface DownloadTool () <NSURLSessionDataDelegate> /** Session会话 */ @property (nonatomic,strong)NSURLSession *session; /** Task任务 */ @property (nonatomic,strong)NSURLSessionDataTask *task; /** 文件的全路径 */ @property (nonatomic,strong)NSString *fileFullPath; /** 传递进度值的block */ @property (nonatomic,copy) SetProgressValue setProgressValue; /** 当前已经下载的文件的长度 */ @property (nonatomic,assign)NSInteger currentFileSize; /** 输出流 */ @property (nonatomic,strong)NSOutputStream *outputStream; /** 不变的文件总长度 */ @property (nonatomic,assign)NSInteger fileTotalSize; @end @implementation DownloadTool + (instancetype)DownloadWithURLString:(NSString*)urlString setProgressValue:(SetProgressValue)setProgressValue{ DownloadTool* download = [[DownloadTool alloc] init]; download.setProgressValue = setProgressValue; [download getFileSizeWithURLString:urlString]; [download creatDownloadSessionTaskWithURLString:urlString]; NSLog(@"%@",download.fileFullPath); return download; } // 刚创建该网络下载工具类的时候,就需要查询本地是否有已经下载的文件,并返回该文件已经下载的长度 -(void)getFileSizeWithURLString:(NSString*)urlString{ // 创建文件管理者 NSFileManager* fileManager = [NSFileManager defaultManager]; // 获取文件各个部分 NSArray* fileComponents = [fileManager componentsToDisplayForPath:urlString]; // 获取下载之后的文件名 NSString* fileName = [fileComponents lastObject]; // 根据文件名拼接沙盒全路径 NSString* fileFullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:fileName]; self.fileFullPath = fileFullPath; NSDictionary* attributes = [fileManager attributesOfItemAtPath:fileFullPath error:nil]; // 如果有该文件,且为下载没完成,就直接拿出该文件的长度设置进度值,并设置当前的文件长度 NSInteger fileCurrentSize = [attributes[@"NSFileSize"] integerValue]; // 如果文件长度为0,就不需要计算进度值了 if (fileCurrentSize != 0) { // 获取最终的文件中长度 NSInteger fileTotalSize = [[ExpendFileAttributes stringValueWithPath:self.fileFullPath key:Key_FileTotalSize] integerValue]; self.currentFileSize = fileCurrentSize; self.fileTotalSize = fileTotalSize; // 设置进度条的值 self.setProgressValue(1.0 * fileCurrentSize / fileTotalSize); } NSLog(@"当前文件长度:%lf" , self.currentFileSize * 1.0); } #pragma mark - 创建网络请求会话和任务,并启动任务 -(void)creatDownloadSessionTaskWithURLString:(NSString*)urlString{ //判断文件是否已经下载完毕 if (self.currentFileSize == self.fileTotalSize && self.currentFileSize != 0) { NSLog(@"文件已经下载完毕"); return; } NSURLSession* session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]]; NSURL* url = [NSURL URLWithString:urlString]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; //2.3 设置请求头 NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentFileSize]; [request setValue:range forHTTPHeaderField:@"Range"]; NSURLSessionDataTask* task = [session dataTaskWithRequest:request]; self.session = session; self.task = task; } #pragma mark - 控制下载的状态 // 开始下载 -(void)startDownload{ [self.task resume]; } // 暂停下载 -(void)suspendDownload{ [self.task suspend]; } #pragma mark - NSURLSessionDataDelegate 的代理方法 // 收到响应调用的代理方法 -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse: (NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{ NSLog(@"执行了收到响应调用的代理方法"); // 创建输出流,并打开流 NSOutputStream* outputStream = [[NSOutputStream alloc] initToFileAtPath:self.fileFullPath append:YES]; [outputStream open]; self.outputStream = outputStream; // 如果当前已经下载的文件长度等于0,那么就需要将总长度信息写入文件中 if (self.currentFileSize == 0) { NSInteger totalSize = response.expectedContentLength; NSString* totalSizeString = [NSString stringWithFormat:@"%ld",totalSize]; [ExpendFileAttributes extendedStringValueWithPath:self.fileFullPath key:Key_FileTotalSize value:totalSizeString]; // 别忘了设置总长度 self.fileTotalSize = totalSize; } // 允许收到响应 completionHandler(NSURLSessionResponseAllow); } // 收到数据调用的代理方法 -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ NSLog(@"执行了收到数据调用的代理方法"); // 通过输出流写入数据 [self.outputStream write:data.bytes maxLength:data.length]; // 将写入的数据的长度计算加进当前的已经下载的数据长度 self.currentFileSize += data.length; // 设置进度值 NSLog(@"当前文件长度:%lf,总长度:%lf",self.currentFileSize * 1.0,self.fileTotalSize * 1.0); NSLog(@"进度值: %lf",self.currentFileSize * 1.0 / self.fileTotalSize); // 获取主线程 NSOperationQueue* mainQueue = [NSOperationQueue mainQueue]; [mainQueue addOperationWithBlock:^{ self.setProgressValue(self.currentFileSize * 1.0 / self.fileTotalSize); }]; } // 数据下载完成调用的方法 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ // 关闭输出流 并关闭强指针 [self.outputStream close]; self.outputStream = nil; // 关闭会话 [self.session invalidateAndCancel]; NSLog(@"%@",[NSThread currentThread]); } -(void)dealloc{ } @end
使用示例源码:
#import "ViewController.h" #import "RainbowProgress.h" #import "DownloadTool.h" #define MP4_URL_String @"http://120.25.226.186:32812/resources/videos/minion_12.mp4" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *showDownloadState; /** 彩虹进度条 */ @property (nonatomic,weak)RainbowProgress *rainbowProgress; /** 网络下载工具对象 */ @property (nonatomic,strong)DownloadTool *download; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self setSelfView]; [self addProgress]; [self addDownload]; } // 启动和关闭的网络下载开关 - (IBAction)SwitchBtn:(UISwitch *)sender { if (sender.isOn) { self.showDownloadState.text = @"开始下载"; [self.download startDownload]; }else{ self.showDownloadState.text = @"暂停下载"; [self.download suspendDownload]; } } #pragma mark - 设置控制器View -(void)setSelfView{ self.view.backgroundColor = [UIColor blackColor]; } #pragma mark - 添加彩虹进度条 -(void)addProgress{ // 创建彩虹进度条,并启动动画 RainbowProgress* rainbowProgress = [[RainbowProgress alloc] init]; [rainbowProgress startAnimating]; [self.view addSubview:rainbowProgress]; self.rainbowProgress = rainbowProgress; } #pragma mark - 创建网络下载任务 -(void)addDownload{ DownloadTool* download = [DownloadTool DownloadWithURLString:MP4_URL_String setProgressValue:^(float progressValue) { self.rainbowProgress.progressValue = progressValue; }]; self.download = download; } #pragma mark - 设置状态栏样式 -(UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleLightContent; } @end
效果图(中间有个过程是重新启动应用程序看看进度条显示的效果,然后继续测试开始下载和暂停下载):
百度云分享源码链接: http://pan.baidu.com/s/1eRwRkZo 密码: 787n
转载请注明出处:/article/5230898.html ,尊重劳动成果。
相关文章推荐
- Tomcat配置https协议详解(菜鸟的不二选择)
- HttpClient post parameters to Https(避免ssl错误)
- 在使用富文本编辑器kindeditor时,使用图片上传的功能报不允许访问路径***的HTTP谓词POST
- Ubuntu 14.04 手动编译安装apache httpd 服务器
- HTTP请求工具类
- TortoiseGit HTTPS方式保存密码最简单的方法
- 分享 C++命名空间的解释 http://blog.chinaunix.net/uid-26874138-id-3215266.html
- 【网络】iOS上传下载
- 知名网络安全公司
- 开源流媒体服务器EasyDarwin支持epoll网络模型,大大提升流媒体服务器网络并发性能
- 基于openssl的https服务配置
- HttpClient 教程
- HttpRequest请求接口
- 用Wireshark简单分析HTTP通信
- 游戏中的网络同步机制——LOCKSTEP
- 推荐大家去博客园看我的博客:http://www.cnblogs.com/bj-xy/点击随笔分类,Python基础学习
- HTTP与HTTPS握手的那些事
- Universal-Image-Loader,android-Volley,Picasso、Fresco和Glide五大Android开源组件加载网络图片的优缺点比较
- inotifyz+rsync实现数据网络推送
- Linux 网络配置