在空闲时间自己编写了一个简单的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. JS滚轮事件(mousewheel/DOMMouseScroll)了解

    已经没有了小学生时代过目不忘的记忆力了,很多自己折腾的东西.接触的东西,短短1年之后就全然不记得了.比方说,完全记不得获取元素与页面距离的方法(getBoundingClientRect),或者是不记 ...

  2. asp.net同时调用JS和后台的无效的解决

    如果js是个定时器,那么就不走后台 <asp:Button runat="server" type="button" Text="重新发送邮件& ...

  3. Java学习——接口Interface

    接口: 初期理解可以认为是一个特殊的抽象类 当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示.class用于定义类interface 用于定义接口 接口定义时,格式特点:1,接口中常量见定 ...

  4. java进程

    package com.process;   public class ProcessTest { public static void main(String[] args) { new Proce ...

  5. MyEclipse安装xfire插件

    xfire因为过时,MyEclipse已经将其支,如果想用只能手动添加了. 安装步骤: 1.点击help┈┈┈→install from site出现下图 在第一栏里,输出http://dist.co ...

  6. hdu3830 (二分+LCA)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Checkers Time Limit: 2000/1000 MS (Java/O ...

  7. UVA 11754 Code Feat (枚举,中国剩余定理)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud C Code Feat   The government hackers at C ...

  8. 在word中使用notepad++实现代码的语法高亮

    转载自:http://blog.csdn.net/woohello/article/details/7621651 有时写文档时需要将代码粘贴到word中,但直接粘贴到word中的代码虽能保持换行与缩 ...

  9. (php)生成指定个数的随机红包

    <?php $total=20;//红包总金额 $num=10;// 分成10个红包,支持10人随机领取 $min=0.01;//每个人最少能收到0.01元 $redpack = new red ...

  10. Integer和Integer数据的大小比较

    public class Test{ public static void main(String[] args){ Integer i1 = 100; Integer i2 = 100; if(i1 ...