iOS开发网络篇—多线程断点下载

说明:本文介绍多线程断点下载。项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件。因为实现过程较为复杂,所以下面贴出完整的代码。

实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分)。

项目中用到的主要类如下:

完成的实现代码如下:

主控制器中的代码:

 1 #import "YYViewController.h"
2 #import "YYFileMultiDownloader.h"
3
4 @interface YYViewController ()
5 @property (nonatomic, strong) YYFileMultiDownloader *fileMultiDownloader;
6 @end
7
8 @implementation YYViewController
9 - (YYFileMultiDownloader *)fileMultiDownloader
10 {
11 if (!_fileMultiDownloader) {
12 _fileMultiDownloader = [[YYFileMultiDownloader alloc] init];
13 // 需要下载的文件远程URL
14 _fileMultiDownloader.url = @"http://192.168.1.200:8080/MJServer/resources/jre.zip";
15 // 文件保存到什么地方
16 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
17 NSString *filepath = [caches stringByAppendingPathComponent:@"jre.zip"];
18 _fileMultiDownloader.destPath = filepath;
19 }
20 return _fileMultiDownloader;
21 }
22
23 - (void)viewDidLoad
24 {
25 [super viewDidLoad];
26
27 }
28
29 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
30 {
31 [self.fileMultiDownloader start];
32 }
33
34 @end

自定义一个基类

YYFileDownloader.h文件

 1 #import <Foundation/Foundation.h>
2
3 @interface YYFileDownloader : NSObject
4 {
5 BOOL _downloading;
6 }
7 /**
8 * 所需要下载文件的远程URL(连接服务器的路径)
9 */
10 @property (nonatomic, copy) NSString *url;
11 /**
12 * 文件的存储路径(文件下载到什么地方)
13 */
14 @property (nonatomic, copy) NSString *destPath;
15
16 /**
17 * 是否正在下载(有没有在下载, 只有下载器内部才知道)
18 */
19 @property (nonatomic, readonly, getter = isDownloading) BOOL downloading;
20
21 /**
22 * 用来监听下载进度
23 */
24 @property (nonatomic, copy) void (^progressHandler)(double progress);
25
26 /**
27 * 开始(恢复)下载
28 */
29 - (void)start;
30
31 /**
32 * 暂停下载
33 */
34 - (void)pause;
35 @end

YYFileDownloader.m文件

1 #import "YYFileDownloader.h"
2
3 @implementation YYFileDownloader
4 @end

下载器类继承自YYFileDownloader这个类

YYFileSingDownloader.h文件

 1 #import "YYFileDownloader.h"
2
3 @interface YYFileSingleDownloader : YYFileDownloader
4 /**
5 * 开始的位置
6 */
7 @property (nonatomic, assign) long long begin;
8 /**
9 * 结束的位置
10 */
11 @property (nonatomic, assign) long long end;
12 @end

YYFileSingDownloader.m文件

  1 #import "YYFileSingleDownloader.h"
2 @interface YYFileSingleDownloader() <NSURLConnectionDataDelegate>
3 /**
4 * 连接对象
5 */
6 @property (nonatomic, strong) NSURLConnection *conn;
7
8 /**
9 * 写数据的文件句柄
10 */
11 @property (nonatomic, strong) NSFileHandle *writeHandle;
12 /**
13 * 当前已下载数据的长度
14 */
15 @property (nonatomic, assign) long long currentLength;
16 @end
17
18 @implementation YYFileSingleDownloader
19
20 - (NSFileHandle *)writeHandle
21 {
22 if (!_writeHandle) {
23 _writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
24 }
25 return _writeHandle;
26 }
27
28 /**
29 * 开始(恢复)下载
30 */
31 - (void)start
32 {
33 NSURL *url = [NSURL URLWithString:self.url];
34 // 默认就是GET请求
35 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
36 // 设置请求头信息
37 NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.begin + self.currentLength, self.end];
38 [request setValue:value forHTTPHeaderField:@"Range"];
39 self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
40
41 _downloading = YES;
42 }
43
44 /**
45 * 暂停下载
46 */
47 - (void)pause
48 {
49 [self.conn cancel];
50 self.conn = nil;
51
52 _downloading = NO;
53 }
54
55
56 #pragma mark - NSURLConnectionDataDelegate 代理方法
57 /**
58 * 1. 当接受到服务器的响应(连通了服务器)就会调用
59 */
60 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
61 {
62
63 }
64
65 /**
66 * 2. 当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)
67 */
68 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
69 {
70 // 移动到文件的尾部
71 [self.writeHandle seekToFileOffset:self.begin + self.currentLength];
72 // 从当前移动的位置(文件尾部)开始写入数据
73 [self.writeHandle writeData:data];
74
75 // 累加长度
76 self.currentLength += data.length;
77
78 // 打印下载进度
79 double progress = (double)self.currentLength / (self.end - self.begin);
80 if (self.progressHandler) {
81 self.progressHandler(progress);
82 }
83 }
84
85 /**
86 * 3. 当服务器的数据接受完毕后就会调用
87 */
88 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
89 {
90 // 清空属性值
91 self.currentLength = 0;
92
93 // 关闭连接(不再输入数据到文件中)
94 [self.writeHandle closeFile];
95 self.writeHandle = nil;
96 }
97
98 /**
99 * 请求错误(失败)的时候调用(请求超时\断网\没有网, 一般指客户端错误)
100 */
101 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
102 {
103
104 }
105
106 @end

设计多线程下载器(利用HMFileMultiDownloader能开启多个线程同时下载一个文件)

一个多线程下载器只下载一个文件

YYFileMultiDownloader.h文件

1 #import "YYFileDownloader.h"
2
3 @interface YYFileMultiDownloader : YYFileDownloader
4
5 @end

YYFileMultiDownloader.m文件

 1 #import "YYFileMultiDownloader.h"
2 #import "YYFileSingleDownloader.h"
3
4 #define YYMaxDownloadCount 4
5
6 @interface YYFileMultiDownloader()
7 @property (nonatomic, strong) NSMutableArray *singleDownloaders;
8 @property (nonatomic, assign) long long totalLength;
9 @end
10
11 @implementation YYFileMultiDownloader
12
13 - (void)getFilesize
14 {
15 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
16 request.HTTPMethod = @"HEAD";
17
18 NSURLResponse *response = nil;
19 #warning 这里要用异步请求
20 [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
21 self.totalLength = response.expectedContentLength;
22 }
23
24 - (NSMutableArray *)singleDownloaders
25 {
26 if (!_singleDownloaders) {
27 _singleDownloaders = [NSMutableArray array];
28
29 // 获得文件大小
30 [self getFilesize];
31
32 // 每条路径的下载量
33 long long size = 0;
34 if (self.totalLength % YYMaxDownloadCount == 0) {
35 size = self.totalLength / YYMaxDownloadCount;
36 } else {
37 size = self.totalLength / YYMaxDownloadCount + 1;
38 }
39
40 // 创建N个下载器
41 for (int i = 0; i<YYMaxDownloadCount; i++) {
42 YYFileSingleDownloader *singleDownloader = [[YYFileSingleDownloader alloc] init];
43 singleDownloader.url = self.url;
44 singleDownloader.destPath = self.destPath;
45 singleDownloader.begin = i * size;
46 singleDownloader.end = singleDownloader.begin + size - 1;
47 singleDownloader.progressHandler = ^(double progress){
48 NSLog(@"%d --- %f", i, progress);
49 };
50 [_singleDownloaders addObject:singleDownloader];
51 }
52
53 // 创建一个跟服务器文件等大小的临时文件
54 [[NSFileManager defaultManager] createFileAtPath:self.destPath contents:nil attributes:nil];
55
56 // 让self.destPath文件的长度是self.totalLengt
57 NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
58 [handle truncateFileAtOffset:self.totalLength];
59 }
60 return _singleDownloaders;
61 }
62
63 /**
64 * 开始(恢复)下载
65 */
66 - (void)start
67 {
68 [self.singleDownloaders makeObjectsPerformSelector:@selector(start)];
69
70 _downloading = YES;
71 }
72
73 /**
74 * 暂停下载
75 */
76 - (void)pause
77 {
78 [self.singleDownloaders makeObjectsPerformSelector:@selector(pause)];
79 _downloading = NO;
80 }
81
82 @end

补充说明:如何获得将要下载的文件的大小?

iOS开发网络篇—多线程断点下载的更多相关文章

  1. iOS开发——网络篇——NSURLSession,下载、上传代理方法,利用NSURLSession断点下载,AFN基本使用,网络检测,NSURLConnection补充

    一.NSURLConnection补充 前面提到的NSURLConnection有些知识点需要补充 NSURLConnectionDataDelegate的代理方法有一下几个 - (void)conn ...

  2. iOS开发网络篇—大文件的多线程断点下载

    http://www.cnblogs.com/wendingding/p/3947550.html iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时 ...

  3. iOS开发网络篇—大文件的多线程断点下载(转)

    http://www.cnblogs.com/wendingding/p/3947550.html   iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了 ...

  4. iOS开发网络篇—使用ASI框架进行文件下载

    iOS开发网络篇—使用ASI框架进行文件下载 说明:本文介绍iOS网络编程中经常用到的框架ASI,如何使用该框架进行文件的下载. 一.简单介绍 代码示例: #import "YYViewCo ...

  5. iOS开发网络篇—数据缓存

      iOS开发网络篇—数据缓存 一.关于同一个URL的多次请求 有时候,对同一个URL请求多次,返回的数据可能都是一样的,比如服务器上的某张图片,无论下载多少次,返回的数据都是一样的. 上面的情况会造 ...

  6. iOS开发网络篇—监测网络状态(转)

    文章转载自:http://www.cnblogs.com/wendingding/p/3950114.html iOS开发网络篇—监测网络状态 一.说明 在网络应用中,需要对用户设备的网络状态进行实时 ...

  7. iOS开发网络篇—简单介绍ASI框架的使用

    iOS开发网络篇—简单介绍ASI框架的使用 说明:本文主要介绍网络编程中常用框架ASI的简单使用. 一.ASI简单介绍 ASI:全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大 ...

  8. iOS开发网络篇—网络编程基础

    iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...

  9. iOS开发网络篇—搭建本地服务器

    iOS开发网络篇—搭建本地服务器 一.简单说明 说明:提前下载好相关软件,且安装目录最好安装在全英文路径下.如果路径有中文名,那么可能会出现一些莫名其妙的问题. 提示:提前准备好的软件 apache- ...

随机推荐

  1. mysql中 的 ENGINE = innodb; 是什么意思?

    存储引擎是innodb.nnoDB 是 MySQL 上第一个提供外键约束的数据存储引擎,除了提供事务处理外,InnoDB 还支持行锁,提供和 Oracle 一样的一致性的不加锁读取,能增加并发读的用户 ...

  2. js如何打印object对象

    js调试中经常会碰到输出的内容是对象而无法打印的时候,光靠alert只能打印出object标示,却不能打印出来里面的内容,甚是不方便,于是各方面整理总结了如下一个函数,能够将数组或者对象这类的结果一一 ...

  3. 当使用VS CODE 时,如果窗口中打开的文件无法识别HTML的话,可以使用以下方法添加要识别的文件类型

    找到该文件并修改\Microsoft VS Code\resources\app\extensions\html\package.json{ "name": "html& ...

  4. php模板引擎

    http://baike.baidu.com/link?url=HmXfdJBv3zpCdnZPeaSmZmqDBHlyTBnz9Rmb5it-jf1_NLHfaku6_i8ssUYbnaTQEBD4 ...

  5. 节点插入--对比jQuery和JavaScript方法(一)

    二.插入元素: 1 <div> 2 <p>面朝大海,春暖花开</p> 3 </div> (一).jQuery方法 1.在节点内部插入: 方法 说明 ap ...

  6. Glibc和GCC,ARM-LINUX-GCC的关系

    看到有些贴子/blog上提到「Glibc编译器」,这是个错误的用语.Glibc不是编译器,Glibc不是编译器,Glibc不是编译器.重要的事情说三遍.GCC才是编译器.

  7. scp 命令使用

    scp 是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且 scp传输是加密的.可能会稍微影响一下速度.当你服 ...

  8. 记录创建企业Wiki的几个开源项目

    很多著名的企业Wiki都是开源产品,如Foswiki(基于Perl),TWiki(基于Perl)等,另外有Mindtouch(基于PHP, C#),Confluence(基于Java)等.维基道的Wi ...

  9. 构建 shiro struts2 spring3 mybatis 的maven项目

    书接上回 构建 struts2 spring3 mybatis 的maven项目 构建 pom.xml 继续在原有框架下 融合shiro ,具体shiro是啥 这里就不解释了,恩 反正功能挺强大的 本 ...

  10. jQuery 之 .stop() 方法

    总结version 1.7版本前.stop([clearQueue][,jumpToEnd])clearQueue: 布尔值,默认是 false;此值处理是相关的动画队列是否移除,如果为 false ...