iOS中的下载管理器(支持断点续传)
在空闲时间自己编写了一个简单的iOS下载管理器。该管理器实现如下功能:
1、能够支持正常的下载,暂停,继续操作。
2、支持断点续传,实现暂停执行继续操作后,依然能正常将文件下载完成。
3、实现实时状态回调,下载进度,速度,一目了然。
准备工作:压缩文件
遇到的主要问题: 拼接到内存中的数据峰值太大,会导致app闪退.
解决办法:
一.(1)用NSFileHandle解决占用内存过大问题(下载一点 写入沙盒一点)
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,copy)NSString *filePath;
/**
* 服务器上面的文件的总大小
*/
@property(nonatomic,assign)long long expectedContentLength;
/**
* 已经接收到的文件的大小
*/
@property(nonatomic,assign)long long hasReceivedContentLength;
/**
* 文件的管道
*/
@property(nonatomic,strong)NSFileHandle *writeFileHandle;
@end
@implementation ViewController
#pragma mark - 懒加载
- (NSString *)filePath{
if (_filePath==nil) {
//获取到Cache目录
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
_filePath = [cache stringByAppendingPathComponent:@"dh.zip"];
}
return _filePath;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"%@",NSHomeDirectory());
}
/**
这个地方不太好
1.没有进度
2.进度峰值太大,会导致我们的app闪退
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost/video.zip"];
//2.NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.只要调用了类方法就会自动发起请求
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
#pragma mark - DownLoadDelegate
/*
这个只会调用一次
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"下载开始!!!");
self.expectedContentLength = response.expectedContentLength;
//1.创建一个0KB的文件,空文件
/**
NSFileManger 做大事,创建,删除,移动我们的文件,并且还可以获取文件的信息
NSFileHandle 做小事,专门用来,流入数据
*/
NSFileManager *manager = [NSFileManager defaultManager];
BOOL createSuccess = [manager createFileAtPath:self.filePath contents:nil attributes:NULL];
//2.创建管道
if (createSuccess) {
NSLog(@"创建文件成功!!!");
self.writeFileHandle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
}
}
/**
这个玩意儿会调用多次
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//NSLog(@"%zd",data.length);
//1.拼接文件的总大小
self.hasReceivedContentLength += data.length;
float progress = (float)self.hasReceivedContentLength / (float)self.expectedContentLength;
NSLog(@"progress==%f",progress);
//2.接收到一点,就写入到沙盒指定的文件里面去一点
//2.1把文件指针,移到已经下载完毕的文件的末尾
[self.writeFileHandle seekToEndOfFile];
//2.2 将新获取到的data 拼接到已经下载完毕的文件的末尾
[self.writeFileHandle writeData:data];
}
/**
只会调用一次
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"下载完毕!!!");
//需要将管道关闭
[self.writeFileHandle closeFile];
}
@end
(2)用NSOutputStream解决占用内存过大问题
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,copy)NSString *filePath;
/**
* 服务器上面的文件的总大小
*/
@property(nonatomic,assign)long long expectedContentLength;
/**
* 已经接收到的文件的大小
*/
@property(nonatomic,assign)long long hasReceivedContentLength;
/**
* 输出,输入是以内存为参照物的
输出,就是将内存中的东西,输出到沙盒
输入,就是将沙盒中的东西输入到内存
*/
@property(nonatomic,strong)NSOutputStream *outputStream;
@end
@implementation ViewController
#pragma mark - 懒加载
- (NSString *)filePath{
if (_filePath==nil) {
//获取到Cache目录
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
_filePath = [cache stringByAppendingPathComponent:@"dh.zip"];
}
return _filePath;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"%@",NSHomeDirectory());
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"%s",__func__);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost/video.zip"];
//2.NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.只要调用了类方法就会自动发起请求
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
//开启子线程的NSRunloop,下载比较特殊,只要下载完毕之后,他的NSRunloop会自动停止
[[NSRunLoop currentRunLoop] run];
});
}
#pragma mark - DownLoadDelegate
/*
这个只会调用一次
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"下载开始!!!");
self.expectedContentLength = response.expectedContentLength;
//1.创建输出流
self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:YES];
//2.打开流
#warning 打开流
[self.outputStream open];
}
/**
这个玩意儿会调用多次
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//NSLog(@"%zd",data.length);
//1.拼接文件的总大小
self.hasReceivedContentLength += data.length;
float progress = (float)self.hasReceivedContentLength / (float)self.expectedContentLength;
NSLog(@"progress==%f %@",progress,[NSThread currentThread]);
//获取一点,就写入沙盒一点
[self.outputStream write:data.bytes maxLength:data.length];
}
/**
只会调用一次
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"下载完毕!!!");
//关闭流
[self.outputStream close];
}
@end
二.文件下载进度(暂停和恢复)
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,copy)NSString *filePath;
/**
* 服务器上面的文件的总大小
*/
@property(nonatomic,assign)long long expectedContentLength;
/**
* 已经接收到的文件的大小
*/
@property(nonatomic,assign)long long hasReceivedContentLength;
/**
* 输出,输入是以内存为参照物的
输出,就是将内存中的东西,输出到沙盒
输入,就是将沙盒中的东西输入到内存
*/
@property(nonatomic,strong)NSOutputStream *outputStream;
/**
* connection
*/
@property(nonatomic,strong)NSURLConnection *connection;
@end
@implementation ViewController
#pragma mark - 懒加载
- (NSString *)filePath{
if (_filePath==nil) {
//获取到Cache目录
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
_filePath = [cache stringByAppendingPathComponent:@"dh.zip"];
}
return _filePath;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",NSHomeDirectory());
}
//开始下载
- (IBAction)start:(id)sender {
NSLog(@"%s",__func__);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//获取到我们的文件的总大小
[self getFileLenght];
//1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost/video.zip"];
//2.NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.只要调用了类方法就会自动发起请求
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
//开启我们子线程的NSRunloop,下载比较特殊,只要下载完毕之后,他的NSRunloop会自动停止
[[NSRunLoop currentRunLoop] run];
});
}
//暂停
- (IBAction)pause:(id)sender {
//取消
//一旦调用了这个方法,connnection就废了
[self.connection cancel];
}
//恢复
- (IBAction)resume:(id)sender {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//先得到文件的大小
[self getFileLenght];
//1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost/video.zip"];
//2.NSURLRequest
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1 设置请求头,告诉服务器一些额外的信息
NSString *rangeValue = [NSString stringWithFormat:@"bytes=%lld-",self.hasReceivedContentLength];
[request setValue:rangeValue forHTTPHeaderField:@"Range"];
//3.只要调用了类方法就会自动发起请求
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
//开启我们子线程的NSRunloop,下载比较特殊,只要下载完毕之后,他的NSRunloop会自动停止
[[NSRunLoop currentRunLoop] run];
});
}
- (void)getFileLenght{
//1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost/video.zip"];
//2.NSURLRequest
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//如果只是获取文件的信息,而不是把数据download下来
request.HTTPMethod = @"HEAD";
//3.发送请求去服务器上面,获取文件的信息
NSURLResponse *response;
NSData *data =[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
NSLog(@"getFileLenght %zd",data.length);
//4.设置给我们文件的总长度
self.expectedContentLength = response.expectedContentLength;
NSLog(@"文件的总大小 %zd",self.expectedContentLength);
}
#pragma mark - DownLoadDelegate
/*
这个只会调用一次
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"下载开始!!!");
NSLog(@"文件总大小----------%lld",response.expectedContentLength);
//1.创建输出流
self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:YES];
//2.打开流
#warning 打开流
[self.outputStream open];
}
/**
这个玩意儿会调用多次
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//NSLog(@"%zd",data.length);
//1.拼接文件的总大小
self.hasReceivedContentLength += data.length;
float progress = (float)self.hasReceivedContentLength / (float)self.expectedContentLength;
NSLog(@"progress==%f %@",progress,[NSThread currentThread]);
//获取一点,就写入沙盒一点
[self.outputStream write:data.bytes maxLength:data.length];
}
/**
只会调用一次
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(@"下载完毕!!!");
//关闭流
[self.outputStream close];
}
@end
三.封装进度条控件
.h文件
/**
XCode6开始,可以动态的在我们Xib里面调试
*/
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface ProgressView : UIView
/**
进度必须在0到1之间
*/
@property(nonatomic,assign) IBInspectable float progress;
/**
* 外环
*/
@property(nonatomic,assign) IBInspectable CGFloat waiHuanLineWidth;
@property(nonatomic,strong) IBInspectable UIColor *huanColor;
@end
.m文件
#import "ProgressView.h"
@implementation ProgressView
- (void)setProgress:(float)progress{
_progress = progress;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
//得到宽度和高度
CGFloat width = rect.size.width;
CGFloat height = rect.size.height;
//1.中点
CGPoint centerPoint = CGPointMake(width *0.5, height*0.5);
//2.外环
//7.线框
if (self.waiHuanLineWidth==0) {
self.waiHuanLineWidth = 5;
}
//3.半径
CGFloat waiHuanRadius = width * 0.5 - self.waiHuanLineWidth*0.5;
//4.画圆
UIBezierPath *waiHuanBezierPath = [UIBezierPath bezierPathWithArcCenter:centerPoint radius:waiHuanRadius startAngle:0 endAngle:M_PI*2 clockwise:YES];
//5.颜色
if (self.huanColor == nil) {
self.huanColor = [UIColor blueColor];
}
//6.设置颜色
[self.huanColor set];
//7.设置外环的宽度
waiHuanBezierPath.lineWidth = self.waiHuanLineWidth;
//8.画出来
[waiHuanBezierPath stroke];
//画矩形
CGFloat cubeWidth = 20;
CGFloat cubeHeight = 20;
CGFloat cubeX = width *0.5 - cubeWidth*0.5;
CGFloat cubeY = height *0.5 - cubeHeight*0.5;
UIBezierPath *cubeBezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(cubeX, cubeY, cubeWidth, cubeHeight)];
[self.huanColor set];
//画出来我们中间的矩形
[cubeBezierPath fill];
//画那个动的圆形
CGFloat neiYuanWidth = self.waiHuanLineWidth;
CGFloat neiYuanRadius = width *0.5 - self.waiHuanLineWidth - neiYuanWidth*0.5;
CGFloat endAngle= M_PI * 2 * self.progress + (- M_PI_2);
UIBezierPath *neiYuanBezierPath = [UIBezierPath bezierPathWithArcCenter:centerPoint radius:neiYuanRadius startAngle:- M_PI_2 endAngle:endAngle clockwise:YES];
[self.huanColor set];
//设置线框
neiYuanBezierPath.lineWidth = neiYuanWidth;
//最后画出来
[neiYuanBezierPath stroke];
}
@end
四.由于时间关系做的不太精致,还有待改善,感兴趣的小伙伴们可以对其进行封装,使其更加的完善!

iOS中的下载管理器(支持断点续传)的更多相关文章
- IIS中发布FTP支持断点续传
IIS10中发布FTP默认就是支持断点续传的.
- iOS中消息传递方式
iOS中消息传递方式 在iOS中有很多种消息传递方式,这里先简单介绍一下各种消息传递方式. 1.通知:在iOS中由通知中心进行消息接收和消息广播,是一种一对多的消息传递方式. NSNotificati ...
- IOS中多版本,多设备类型支持注意事项
IOS系统从07年出来,到现在也有6年了,每年发布一次到两次新的设备,从iPhone1,iPhone2 ... iPhone4s再到最新的iPhone5.硬件在升级的过程中CPU的架构也可能发生变化, ...
- ios中safari无痕浏览模式下,localStorage的支持情况
前言 前阶段,测试提了个bug,在苹果手机中无痕模式下,搜索按钮不好使,无法跳页,同时搜索历史也没有展示(用户搜索历史时使用localStorage存储). 正文 iOS上Sarfari在无痕模式下, ...
- IOS中input键盘事件支持的解决方法
欢迎大家去我的网站详细查看http://genghongshuo.com.cn/ IOS中input键盘事件keyup.keydown.等支持不是很好, 用input监听键盘keyup事件,在安卓手机 ...
- ios设备中openGL所支持的最大纹理尺寸
这几天碰到一个在iphone4上显示图片未黑色矩形的bug,在其他机器上都正常 最后发现是图片打包尺寸的关系,iphone4无法读取2048以上大小的单个图片,所以其中的图片都显示成了黑色,希望对碰到 ...
- iOS中如何让TextView和TextField控件支持return键收起输入法
TextView和TextField控件是iOS中负责接收用户输入的控件,那当用户输入完成时怎么收起面板呢? 1.TextView和TextField控件获得焦点之后的第一反应就是弹出输入法面板: 2 ...
- ios 中不new Date 的格式 不支持年月日 以‘-’ 分割的格式
new Date("2018-1-5") 在 ios 中显示 invalid date - 换做 / 则可以顺利显示 new Date("2018/1/5")
- iOS中支付宝集成
iOS中支付宝集成 如今各种的App中都使用了三方支付的功能,现在将我在使用支付宝支付集成过程的心得分享一下,希望对大家都能有所帮助 要集成一个支付宝支付过程的环境,大致需要: 1>公司:先与支 ...
随机推荐
- Android--------Java接口回调
>###回调说明 回调说明: 1.class A,class B 2.class A 实现接口callback 3.class B拥有一个参数为c ...
- Android HttpClient框架get和post方式提交数据(非原创)
1.fragment_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android& ...
- SQL Server -查看数据库扩展属性
1.fn_listextendedproperty 函数可以基于对象类型显示单个数据库对象或数据库中所有对象的扩展属性.例如,可以返回表或表中所有列的扩展属性. A.下面的示例显示了数据库本身设置的所 ...
- css基础-背景文本
css背景 1. background-color:#6495ed; 2. background-image:url('bgdesert.jpg'); 3. background-repeat:rep ...
- UIView的一些常用属性和方法
UIView的一些常用属性和方法 1. UIView的属性 UIView继承自UIResponder,拥有touches方法. - (instancetype)initWithFrame:(CGRec ...
- php缩放gif和png格式透明背景变成黑色的解决方法
在对gif或png格式的图片进行缩放等操作时,原本透明背景的图片最后都变成黑色的,解决办法 $img = imagecreatetruecolor(, ); //2.上色 $color=imageco ...
- 一些常用Linux命令简记
1.重命名文件夹 mv xxx/ yyy/ 将xxx文件夹重命名为yyy(前提是当前目录没有yyy文件夹,否则就移进去了!) 2.数据盘重新挂载 一.# umount /mnt(卸载硬盘已挂载的mn ...
- 傻瓜式硬盘重装win7系统图文加视频教程
标准适用环境: win7系统还能用,但是想重装win7系统.[当然win7进不去也可以在PE进行] 其它 需要工具: win7 ISO镜像 | [ w ...
- GitLab CI
GitLab CI持续集成配置方案 目录 1. 持续集成介绍 1.1 概念 1.2 持续集成的好处 2. GitLab持续集成(CI) 2.1 简介 2.2 GitLab简单原理图 2.3 Git ...
- ORA-14400: inserted partition key does not map to any partition
ORA-14400: inserted partition key does not map to any partition 数据库表已经分区,如果插入数据时出现错误提示: ORA-14400: 插 ...