在空闲时间自己编写了一个简单的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中的下载管理器(支持断点续传)的更多相关文章

  1. IIS中发布FTP支持断点续传

    IIS10中发布FTP默认就是支持断点续传的.

  2. iOS中消息传递方式

    iOS中消息传递方式 在iOS中有很多种消息传递方式,这里先简单介绍一下各种消息传递方式. 1.通知:在iOS中由通知中心进行消息接收和消息广播,是一种一对多的消息传递方式. NSNotificati ...

  3. IOS中多版本,多设备类型支持注意事项

    IOS系统从07年出来,到现在也有6年了,每年发布一次到两次新的设备,从iPhone1,iPhone2 ... iPhone4s再到最新的iPhone5.硬件在升级的过程中CPU的架构也可能发生变化, ...

  4. ios中safari无痕浏览模式下,localStorage的支持情况

    前言 前阶段,测试提了个bug,在苹果手机中无痕模式下,搜索按钮不好使,无法跳页,同时搜索历史也没有展示(用户搜索历史时使用localStorage存储). 正文 iOS上Sarfari在无痕模式下, ...

  5. IOS中input键盘事件支持的解决方法

    欢迎大家去我的网站详细查看http://genghongshuo.com.cn/ IOS中input键盘事件keyup.keydown.等支持不是很好, 用input监听键盘keyup事件,在安卓手机 ...

  6. ios设备中openGL所支持的最大纹理尺寸

    这几天碰到一个在iphone4上显示图片未黑色矩形的bug,在其他机器上都正常 最后发现是图片打包尺寸的关系,iphone4无法读取2048以上大小的单个图片,所以其中的图片都显示成了黑色,希望对碰到 ...

  7. iOS中如何让TextView和TextField控件支持return键收起输入法

    TextView和TextField控件是iOS中负责接收用户输入的控件,那当用户输入完成时怎么收起面板呢? 1.TextView和TextField控件获得焦点之后的第一反应就是弹出输入法面板: 2 ...

  8. ios 中不new Date 的格式 不支持年月日 以‘-’ 分割的格式

    new Date("2018-1-5") 在 ios 中显示 invalid date - 换做 / 则可以顺利显示 new Date("2018/1/5")

  9. iOS中支付宝集成

    iOS中支付宝集成 如今各种的App中都使用了三方支付的功能,现在将我在使用支付宝支付集成过程的心得分享一下,希望对大家都能有所帮助 要集成一个支付宝支付过程的环境,大致需要: 1>公司:先与支 ...

随机推荐

  1. 数据结构算法及应用——二叉树

    一.二叉树性质 特性1 包含n (n> 0 )个元素的二叉树边数为n-1 特性2 二叉树的高度(height)或深度(depth)是指该二叉树的层数(有几层元素,而不是有层的元素间隔) 特性3 ...

  2. uva 1597 Searching the Web

    The word "search engine" may not be strange to you. Generally speaking, a search engine se ...

  3. 不用不知道 apply()与call()的强大

    在看关于javascript继承的时候 好多地方都用到了apply()和call() 之前在简单编程的时候根本没有遇到过 查阅资料后才发现这两个方法是如此的好用. 下面从几方面来看一下这两个方法: 1 ...

  4. jq插件处女座 图片轮播

    好久没写博客了,变得好懒呀,无地自容.最近一直在学sass和jq插件的写法,照猫画虎的谢了一个jq的插件,也算是第一次真正称得上插件的插件 ,废话不多说 上代码 (function($) { $.fn ...

  5. js中的this指向

    1, 指向window 全局变量 alert(this) //返回 [object Window] 全局函数 function sayHello(){ alert(this); } sayHello( ...

  6. linux bugfree 安装

    前段时间用了下bugzilla,请参考:linux bugzilla nginx 安装配置 详解,感觉不是很好用.下面说一下,bugfree的安装 bugfree3.0.1是用php的yii框架开发的 ...

  7. ni

    坚强歌词 马天宇 - 坚强 天使的翅膀挥动着的光芒一路走来学会了坚强每一次你努力认真的模样让我很欣赏 雨天的路上会有一缕阳光温暖被淋湿的希望再小的河也能汇成海洋让我去远航 一路上陪伴我的目光是最感动的 ...

  8. c# Parallel并行运算

    string str = ""; DataTable dt=new DataTable(); dt.Columns.Add("name", typeof(Sys ...

  9. Keil中如何消除UNCALLED SEGMENT,IGNORED FOR OVERLAY PROCESS警告

    在Keil C中,如果没有显式调用到定义过的函数,就会出现这样的的警告.当出现这样的警告时,可以不用管,因为不影响其它部分.但是,我们知道,即使没有调用这个函数,Keil仍然把它编译连接进整个程序,不 ...

  10. qt 多线程之间通讯

    问题描述:界面线程MainApp为主线程,工作线程MyThread为一子线程,从工作线程向主线程传递字符串用于在主线程中显示. Qt的信号与槽机制可以将任何继承自QObject类的对象捆绑在一起,使不 ...