iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>
前言:根据前篇《iOS开发之网络编程--2、NSURLSessionDownloadTask文件下载》或者《iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)》,都遗留了一个细节未处理的问题,那就是在离线断点下载的过程中,当应用程序重新启动之后,进度条的进度值默认没有设置为之前已经下载的进度,根据基本公式"当前进度值 = 已经下载的数据长度 ÷ 最终下载完的数据总长度",已经下载的数据长度可以由沙盒中已经下载的那部分数据获取,但是最终下载完的数据总长度就需要通过网络返回的信息了,但是别忘了,每一次重新启动应用程序初始状态默认都是暂停下载,或者是断网的情况下无法请求网络数据,那么如何获取这个"最终下载完的数据总长度"呢?
本篇还涉及到在子线程创建下载任务,然后通过线程通知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 != ) {
// 获取最终的文件中长度
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 != ) {
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 == ) {
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
转载请注明出处:http://www.cnblogs.com/goodboy-heyang/p/5200873.html ,尊重劳动成果。
iOS开发之网络编程--4、NSURLSessionDataTask实现文件下载(离线断点续传下载) <进度值显示优化>的更多相关文章
- iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载+使用输出流代替文件句柄
前言:本篇讲解,在前篇iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载的基础上,使用输出流代替文件句柄实现大文件断点续传. 在实际开发中,输入输出流用的比较少,但 ...
- iOS开发之网络编程--3、NSURLSessionDataTask实现文件下载(离线断点续传下载)
前言:使用NSURLSessionDownloadTask满足不这个需要离线断点续传的下载需求,所以这里就需要使用NSURLSessionDataTask的代理方法来处理下载大文件,并且实现离线断点续 ...
- iOS开发之网络编程--2、NSURLSessionDownloadTask文件下载
本文内容大纲: 1.回顾NSURLSessionTask 2.NSURLSessionDownloadTask大文件之block下载 3.NSURLSessionDownloadTask大文件之代理方 ...
- iOS开发之网络编程--使用NSURLConnection实现大文件下载
主要思路(实现下载数据分段写入缓存中) 1.使用NSURLConnectionDataDelegate以及代理方法.2.在成功获取响应的代理方法中,获得沙盒全路径,并在该路径下创建空文件和文件句柄.3 ...
- iOS开发之网络编程--5、NSURLSessionUploadTask+NSURLSessionDataDelegate代理上传
前言:关于NSURLSession的主要内容快到尾声了,这里就讲讲文件上传.关于文件上传当然就要使用NSURLSessionUploadTask,这里直接讲解常用的会和代理NSURLSessionDa ...
- iOS开发之网络编程--使用NSURLConnection实现文件上传
前言:使用NSURLConnection实现文件上传有点繁琐. 本文并没有介绍使用第三方框架上传文件. 正文: 这里先提供用于编码测试的接口:http://120.25.226.186:3281 ...
- iOS开发之网络编程--小文件下载
文件下载方式: 如果下载的文件比较小,下载方式: 直接用NSData的 +(id)dataWithContentsOfURL:(NSURL*)url; 利用NSURLConnection发送一个HTT ...
- IOS开发之网络编程--文件压缩和解压缩
前言: QQ表情包就用到了解压缩,从网络下载的那么多表情文件格式并不是一个一个图片文件,而是多个图片压缩而成的表情压缩包.下面介绍的是iOS开发中会用到的压缩和解压缩的第三方框架的使用. 注意: 这个 ...
- iOS开发之网络编程--XCode7 更新以来需要手动设置的内容
XCode7 更新以来,默认是不允许加载一些http网络请求,是因为现在网络大部分使用更安全的https协议头. 所以,iOS网络编程如果出现请求无效,事先考虑是否设置一下了以下操作:
随机推荐
- Linux shell tee指令学习
转载自:http://blog.163.com/xujian900308@126/blog/static/12690761520129911304568/ tee tee:读取标准输入的数据,并将 ...
- 【转载】关于 Ubuntu 的小知识分享
转载自:http://os.51cto.com/art/201307/402197.htm 一.默认开机直接进入到Ubuntu命令行界面 安装Ubuntu后,开机会默认进入到图形界面,如果不喜欢图形界 ...
- EPANET能做什么,不能做什么
What Epanet cand and cannot do Good news!Epanet can do most of the calculations you may need for you ...
- JS魔法堂:IE5~9的Drag&Drop API
一.前言 < HTML5魔法堂:全面理解Drag & Drop API>中提到从IE5开始已经支持DnD API,但IE5~9与HTML5的API有所不同,下面我们来了解一 ...
- Qt之QAbstractItemView视图项拖拽(一)
一.需求说明 最近在搞视图项的拖拽,也上网查了一些资料,好多的文档都是一样的,只是被不通的网站所收录了(也有可能是被爬过去的,不明所以),不过也有一些文档写的不错,不过就是太简易,都是点睛之笔,总之功 ...
- 登陆mysql时提示异常的解决方法
[root@host2 ~]# mysql -uroot -p Enter password: ERROR (HY000): Can't connect to local MySQL server t ...
- 使用Swift操作NSDate类型基础
时间类型是我们在处理业务的时候使用非常频繁的一个数据类型.下面我们看一下时间NSDate的基本使用方法. 1.比较大小 我比较擅长.NET,我们知道C#里面DateTime类型可以使用"&g ...
- Oracle Fusion Applications (11.1.8) Media Pack and Oracle Application Development Framework 11g (11.1.1.7.2) for Microsoft Windows x64 (64-bit)
Oracle Fusion Applications (11.1.8) Media Pack for Microsoft Windows x64 (64-bit) 重新搜索 常见问题 提示 ...
- 一个Linq表达式的扩展函数帮助类
/// <summary> /// Linq表达式的扩展函数 /// </summary> public static class ExpressionExtensions { ...
- 使用VS GDB扩充套件在VS上远端侦错Linux上的C/C++程序
在 Linux 上开发 C/C++ 程序,或许你会直接(本机或远端)登入 Linux,打开编辑器写完代码后,就用 gcc/g++ 来编译,遇到要除错(debug)的时候,则会选择使用 gdb 来进行除 ...