A.需求
- 边下边写入硬盘
- 显示下载进度
- 暂停/恢复 下载
- 解压文件
- 多线程下载
B.基本知识
1.小文件下载
如果文件比较小,下载方式会比较多
直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
利用NSURLConnection发送一个HTTP请求去下载
如果是下载图片,还可以利用SDWebImage框架
2.HTTP的Range头信息
通过设置请求头Range可以指定每次从网路下载数据包的大小
Range示例
bytes=0-499 从0到499的头500个字节
bytes=500
-
999 从500到999的第二个500字节
bytes=500- 从500字节以后的所有字节
bytes=-500 最后500个字节
bytes=500-599,800-899 同时指定几个范围
Range小结
- 用于分隔
前面的数字表示起始字节数
后面的数组表示截止字节数,没有表示到末尾
, 用于分组,可以一次指定多个Range,不过很少用
3.第三方解压框架SSZipArchive
https://github.com/samsoffes/ssziparchive
注意:
需要引入libz.dylib框架
1 // Unzipping
2 NSString *zipPath = @"path_to_your_zip_file";
3 NSString *destinationPath = @"path_to_the_folder_where_you_want_it_unzipped";
4 [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
5
6 // Zipping
7 NSString *zippedPath = @"path_where_you_want_the_file_created";
8 NSArray *inputPaths = [NSArray arrayWithObjects:
9 [[NSBundle mainBundle] pathForResource:@"photo1" ofType:@"jpg"],
10 [[NSBundle mainBundle] pathForResource:@"photo2" ofType:@"jpg"]
11 nil];
12 [SSZipArchive createZipFileAtPath:zippedPath withFilesAtPaths:inputPaths];
C.实现
1.使用NSFileHandle实现边下边写入硬盘
1 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
2 NSLog(@"开始接收");
3
4 // 获取存放路径
5 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
6 NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
7
8 // 创建一个空的文件,用来存放接收的数据
9 NSFileManager *manager = [NSFileManager defaultManager];
10 [manager createFileAtPath:filePath contents:nil attributes:nil];
11
12 // 把文件句柄用写入方式指向文件
13 self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
14
15 }
16
17 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
18 NSLog(@"正在接收: %d byte", data.length);
19
20 // 移动句柄到上次写入数据的末位置
21 [self.handle seekToEndOfFile];
22
23 // 写入数据
24 [self.handle writeData:data];
25 }
2.显示下载进度条
使用ProgressView
从response得到文件大小,再累加每次接收到的数据,计算出完成百分比
1 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
2 NSLog(@"开始接收");
3
4 // 获取存放路径
5 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
6 NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
7
8 // 创建一个空的文件,用来存放接收的数据
9 NSFileManager *manager = [NSFileManager defaultManager];
10 [manager createFileAtPath:filePath contents:nil attributes:nil];
11
12 // 把文件句柄用写入方式指向文件
13 self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
14
15 // 复位进度条
16 self.progressView.progress = 0.0;
17
18 // 复位当前下载量
19 self.currentDataLength = 0;
20
21 // 得到总的文件大小
22 self.totalDataLength = response.expectedContentLength;
23
24
25 }
26
27 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
28 NSLog(@"正在接收: %d byte", data.length);
29 // 移动句柄到上次写入数据的末位置
30 [self.handle seekToEndOfFile];
31
32 // 写入数据
33 [self.handle writeData:data];
34
35 // 下载量累加
36 self.currentDataLength += data.length;
37
38 // 刷新进度条
39 CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
40 NSLog(@"completed percentage: %f%%", completedProgress * 100);
41 self.progressView.progress = completedProgress;
42 }
3.暂停/恢复下载
使用成员属性标记下载状态
请求得到文件头信息,根据当前下载量,设置请求request头信息,从上次下载完成的地方开始继续下载
注意:NSURLConnection 取消cancel之后不能恢复,只能再创建
1 //
2 // BigFileDownloadViewController.m
3 // BigFileDownloadDemo
4 //
5 // Created by hellovoidworld on 15/1/26.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "BigFileDownloadViewController.h"
10
11 @interface BigFileDownloadViewController ()
12
13 /** 进度条 */
14 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
15
16 /** 文件句柄 */
17 @property(nonatomic, strong) NSFileHandle *handle;
18
19 /** 当前完成的下载量 */
20 @property(nonatomic, assign) long long currentDataLength;
21
22 /** 总的文件大小 */
23 @property(nonatomic, assign) long long totalDataLength;
24
25 /** 当前下载连接 */
26 @property(nonatomic, strong) NSURLConnection *conn;
27
28 /** 下载状态标识 */
29 @property(nonatomic, assign, getter=isDownloading) BOOL downloading;
30
31 /** 下载/暂停 按钮事件 */
32 - (IBAction)download:(UIButton *)button;
33
34 @end
35
36 @implementation BigFileDownloadViewController
37
38 - (void)viewDidLoad {
39 [super viewDidLoad];
40 // Do any additional setup after loading the view.
41 }
42
43
44 - (IBAction)download:(UIButton *)button {
45 if (self.isDownloading) { // 如果正在下载中,暂停下载
46
47 self.downloading = NO;
48 [button setTitle:@"开始" forState:UIControlStateNormal];
49
50 // 取消连接,不能恢复
51 [self.conn cancel];
52 self.conn = nil;
53 } else { // 如果是暂停中,恢复下载
54
55 self.downloading = YES;
56 [button setTitle:@"暂停" forState:UIControlStateNormal];
57
58 // 从上次下载完成的地方继续下载,初始就是0
59 NSURL *url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/videos/0627.zip"];
60 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
61
62 // 设置request头信息,指明要从文件哪里开始下载
63 NSString *value = [NSString stringWithFormat:@"bytes=%lld-", self.currentDataLength];
64 [request setValue:value forHTTPHeaderField:@"Range"];
65
66 self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
67 }
68 }
69
70 #pragma mark - NSURLConnectionDataDelegate
71 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
72 NSLog(@"失败");
73 NSLog(@"%@", error);
74 }
75
76 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
77 NSLog(@"开始接收");
78
79 // 如果是继续下载,就不要再重复创建文件了
80 if (self.totalDataLength) return;
81
82 // 获取存放路径
83 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
84 NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
85
86 // 创建一个空的文件,用来存放接收的数据
87 NSFileManager *manager = [NSFileManager defaultManager];
88 if ([manager fileExistsAtPath:filePath]) {
89 [manager removeItemAtPath:filePath error:nil];
90 }
91
92 // 刚创建默认是0字节
93 [manager createFileAtPath:filePath contents:nil attributes:nil];
94
95
96 // 把文件句柄用写入方式指向文件
97 self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
98
99 // 得到总的文件大小
100 self.totalDataLength = response.expectedContentLength;
101
102
103 }
104
105 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
106 NSLog(@"正在接收: %d byte", data.length);
107
108 // 下载量累加
109 self.currentDataLength += data.length;
110
111 // 刷新进度条
112 CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
113 NSLog(@"completed percentage: %f%%", completedProgress * 100);
114 self.progressView.progress = completedProgress;
115
116 // 移动句柄到上次写入数据的末位置
117 [self.handle seekToEndOfFile];
118
119 // 写入数据
120 [self.handle writeData:data];
121 }
122
123 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
124 NSLog(@"接收完毕");
125
126 // 复位当前下载量
127 self.currentDataLength = 0;
128
129 // 复位下载文件大小
130 self.totalDataLength = 0;
131
132 // 关闭连接
133 [self.handle closeFile];
134 self.handle = nil;
135 }
136
137
138 @end
4.文件解压/压缩
使用ssziparchive-master框架
注意: 需要引入libz.dylib框架
(1)解压
使用block存储下载完成后的操作:解压
1 // 完成下载后block
2 self.downloader.didFinishHandler = ^{
3 // 解压文件
4 [SSZipArchive unzipFileAtPath:filePath toDestination:cachePath];
5 NSLog(@"解压完成!");
6 };
#mark: 我使用大小为1.4G,仅包含一个视频的压缩文件进行解压,没有解出来; 解压包含了几张图片的压缩包能正常工作。
(2)压缩
a.准备实验文件
b.使用SSZipArchive归档解压
1 // 获得所有png图片,注意这里是不能获得沙盒中的文件的,只能是项目中的
2 NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"PNG" inDirectory:nil];
3 NSLog(@"%@", pngs);
4
5 // 压缩文件存放路径
6 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
7 NSString *filePath = [cachePath stringByAppendingPathComponent:@"pngs.zip"];
8
9 // 压缩文件
10 [SSZipArchive createZipFileAtPath:filePath withFilesAtPaths:pngs];
11
12 NSLog(@"压缩完成!");
5.下载器的封装
将下载功能封装成一个类,传入请求url和文件存放地址,调用接口开启、暂停线程
1 //
2 // FileDownloader.h
3 // BigFileDownloadDemo
4 //
5 // Created by hellovoidworld on 15/1/26.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import <Foundation/Foundation.h>
10
11 @interface FileDownloader : NSObject
12
13 /** 请求路径 */
14 @property(nonatomic, strong) NSURL *url;
15
16 /** 存放路径 */
17 @property(nonatomic, strong) NSString *filePath;
18
19 /** 下载状态 */
20 @property(nonatomic, readonly, getter=isDownloading) BOOL downloading;
21
22 /** 更新进度block */
23 @property(nonatomic, copy) void(^progressHandler)(double progress);
24
25 /** 完成下载后block */
26 @property(nonatomic, copy) void(^didFinishHandler)();
27
28 - (void) startDownloading;
29 - (void) pauseDownloading;
30
31 @end
1 //
2 // FileDownloader.m
3 // BigFileDownloadDemo
4 //
5 // Created by hellovoidworld on 15/1/26.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "FileDownloader.h"
10 #import <UIKit/UIKit.h>
11
12 @interface FileDownloader() <NSURLConnectionDataDelegate>
13
14 /** 文件句柄 */
15 @property(nonatomic, strong) NSFileHandle *handle;
16
17 /** 当前完成的下载量 */
18 @property(nonatomic, assign) long long currentDataLength;
19
20 /** 总的文件大小 */
21 @property(nonatomic, assign) long long totalDataLength;
22
23 /** 当前下载连接 */
24 @property(nonatomic, strong) NSURLConnection *conn;
25
26 @end
27
28 @implementation FileDownloader
29
30 /** 开始下载 */
31 - (void) startDownloading {
32 _downloading = YES;
33
34 // 从上次下载完成的地方继续下载,初始就是0
35 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url];
36
37 // 设置request头信息,指明要从文件哪里开始下载
38 NSString *value = [NSString stringWithFormat:@"bytes=%lld-", self.currentDataLength];
39 [request setValue:value forHTTPHeaderField:@"Range"];
40
41 self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
42 }
43
44 /** 暂停下载 */
45 - (void) pauseDownloading {
46 _downloading = NO;
47
48 // 取消连接,不能恢复
49 [self.conn cancel];
50 self.conn = nil;
51 }
52
53 #pragma mark - NSURLConnectionDataDelegate
54 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
55 NSLog(@"失败");
56 NSLog(@"%@", error);
57 }
58
59 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
60 NSLog(@"开始接收");
61
62 // 如果是继续下载,就不要再重复创建文件了
63 if (self.totalDataLength) return;
64
65 // 创建一个空的文件,用来存放接收的数据
66 NSFileManager *manager = [NSFileManager defaultManager];
67 if ([manager fileExistsAtPath:self.filePath]) {
68 [manager removeItemAtPath:self.filePath error:nil];
69 }
70
71 // 刚创建默认是0字节
72 [manager createFileAtPath:self.filePath contents:nil attributes:nil];
73
74
75 // 把文件句柄用写入方式指向文件
76 self.handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
77
78 // 得到总的文件大小
79 self.totalDataLength = response.expectedContentLength;
80
81
82 }
83
84 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
85 NSLog(@"正在接收: %d byte", data.length);
86
87 // 下载量累加
88 self.currentDataLength += data.length;
89
90 // 刷新进度条
91 CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
92 NSLog(@"completed percentage: %f%%", completedProgress * 100);
93 if (self.progressHandler) {
94 self.progressHandler(completedProgress);
95 }
96
97 // 移动句柄到上次写入数据的末位置
98 [self.handle seekToEndOfFile];
99
100 // 写入数据
101 [self.handle writeData:data];
102
103 NSLog(@"现在的线程:%@", [NSThread currentThread]);
104 }
105
106 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
107 NSLog(@"接收完毕");
108
109 // 复位当前下载量
110 self.currentDataLength = 0;
111
112 // 复位下载文件大小
113 self.totalDataLength = 0;
114
115 // 关闭连接
116 [self.handle closeFile];
117 self.handle = nil;
118
119 if (self.didFinishHandler) {
120 self.didFinishHandler();
121 }
122 }
123
124 @end
1 //
2 // BigFileDownloadViewController.m
3 // BigFileDownloadDemo
4 //
5 // Created by hellovoidworld on 15/1/26.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "BigFileDownloadViewController.h"
10 #import "FileDownloader.h"
11 #import "SSZipArchive.h"
12
13 @interface BigFileDownloadViewController ()
14
15 /** 进度条 */
16 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
17
18 /** 下载器 */
19 @property(nonatomic, strong) FileDownloader *downloader;
20
21 /** 下载/暂停 按钮事件 */
22 - (IBAction)download:(UIButton *)button;
23
24 @end
25
26 @implementation BigFileDownloadViewController
27
28 - (void)viewDidLoad {
29 [super viewDidLoad];
30 // Do any additional setup after loading the view.
31
32 // 设置下载器请求路径
33 self.downloader = [[FileDownloader alloc] init];
34 self.downloader.url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/videos/0627.zip"];
35 // self.downloader.url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/images/images.zip"];
36
37 // 设置存放路径
38 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
39 NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
40 // NSString *filePath = [cachePath stringByAppendingPathComponent:@"images.zip"];
41 self.downloader.filePath = filePath;
42
43
44 // 刷新进度条的block
45 typeof(self) vc = self;
46 self.downloader.progressHandler = ^(double progress) {
47 vc.progressView.progress = progress;
48 };
49
50 // 完成下载后block
51 self.downloader.didFinishHandler = ^{
52 // 解压文件
53 [SSZipArchive unzipFileAtPath:filePath toDestination:cachePath];
54 NSLog(@"解压完成!");
55 };
56 }
57
58
59 - (IBAction)download:(UIButton *)button {
60 if (self.downloader.isDownloading) { // 如果正在下载中,暂停下载
61 [button setTitle:@"开始" forState:UIControlStateNormal];
62 [self.downloader pauseDownloading];
63 } else { // 如果是暂停中,恢复下载
64 [button setTitle:@"暂停" forState:UIControlStateNormal];
65 [self.downloader startDownloading];
66 }
67 }
68
69
70 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
71
72 // 获得所有png图片
73 NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"PNG" inDirectory:nil];
74 NSLog(@"%@", pngs);
75
76 // 压缩文件存放路径
77 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
78 NSString *filePath = [cachePath stringByAppendingPathComponent:@"pngs.zip"];
79
80 // 压缩文件
81 [SSZipArchive createZipFileAtPath:filePath withFilesAtPaths:pngs];
82
83 NSLog(@"压缩完成!");
84 }
85
86 @end
6.多线程下载
思路:由于调用NSURLConnection的代理方法进行服务器请求和接收,发送的是异步请求,所以可以认为利用几个NSURLConnection同时发送请求,下载一个文件的不同部分,就能实现多线程下载。
- 创建多线下载器类和单线下载器
- 多线下载器对象创建n个单线下载器来进行下载
- 创建一个跟实际文件大小一样的临时文件,用来辅助定位单线下载器的下载存储位置
- n个单线下载器负责一个文件的不同部分的下载
- 使用请求头设置Range来实现文件下载部分的不同
(1)基本下载器
用来声明了一些公用属性和方法
1 //
2 // FileDownloader.h
3 // BigFileDownloadDemo
4 //
5 // Created by hellovoidworld on 15/1/26.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import <Foundation/Foundation.h>
10
11 @interface FileDownloader : NSObject
12 {
13 @protected
14 BOOL _downloading;
15 }
16
17 /** 请求路径 */
18 @property(nonatomic, strong) NSString *url;
19
20 /** 存放路径 */
21 @property(nonatomic, strong) NSString *filePath;
22
23 /** 下载状态 */
24 @property(nonatomic, readonly, getter=isDownloading) BOOL downloading;
25
26 /** 更新进度block */
27 @property(nonatomic, copy) void(^progressHandler)(double progress);
28
29 /** 完成下载后block */
30 @property(nonatomic, copy) void(^didFinishHandler)();
31
32 - (void) startDownloading;
33 - (void) pauseDownloading;
34
35 @end
(2)单线下载器
1 //
2 // SingleDownloader.h
3 // MultiDownloaderDemo
4 //
5 // Created by hellovoidworld on 15/1/27.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "FileDownloader.h"
10
11 @interface SingleDownloader : FileDownloader
12
13 /** 开始下载的位置(字节) */
14 @property(nonatomic, assign) long long begin;
15
16 /** 结束下载的位置(字节) */
17 @property(nonatomic, assign) long long end;
18
19 @end
1 //
2 // SingleDownloader.m
3 // MultiDownloaderDemo
4 //
5 // Created by hellovoidworld on 15/1/27.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "SingleDownloader.h"
10
11 @interface SingleDownloader() <NSURLConnectionDataDelegate>
12
13 /** 文件句柄 */
14 @property(nonatomic, strong) NSFileHandle *handle;
15
16 /** 当前下载连接 */
17 @property(nonatomic, strong) NSURLConnection *conn;
18
19 /** 当前下载量 */
20 @property(nonatomic, assign) long long currentLength;
21
22 @end
23
24 @implementation SingleDownloader
25
26 /** 初始化文件句柄 */
27 - (NSFileHandle *)handle {
28 if (nil == _handle) {
29 _handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
30 }
31 return _handle;
32 }
33
34 /** 开始下载 */
35 - (void) startDownloading {
36 // 从上次下载完成的地方继续下载,初始就是0
37 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
38
39 // 设置request头信息,指明要从文件哪里开始下载
40 NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.begin, self.end];
41 [request setValue:value forHTTPHeaderField:@"Range"];
42
43 self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
44
45 _downloading = YES;
46 }
47
48 /** 暂停下载 */
49 - (void) pauseDownloading {
50 // 取消连接,不能恢复
51 [self.conn cancel];
52 self.conn = nil;
53
54 _downloading = NO;
55 }
56
57 #pragma mark - NSURLConnectionDataDelegate
58 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
59 NSLog(@"失败");
60 NSLog(@"%@", error);
61 }
62
63 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
64 NSLog(@"开始接收");
65 }
66
67 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
68 // 移动句柄到上次写入数据的末位置
69 [self.handle seekToFileOffset:self.begin + self.currentLength];
70
71 // 写入数据
72 [self.handle writeData:data];
73
74 // 已经下载的量
75 self.currentLength += data.length;
76
77 if (self.progressHandler) {
78 double progress = (double)self.currentLength / (self.end - self.begin + 1);
79 self.progressHandler(progress);
80 }
81 }
82
83 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
84 NSLog(@"接收完毕");
85
86 // 清空下载量
87 self.currentLength = 0;
88
89
90 // 关闭连接
91 [self.handle closeFile];
92 self.handle = nil;
93
94 if (self.didFinishHandler) {
95 self.didFinishHandler();
96 }
97 }
98
99
100 @end
(3)总下载器
1 //
2 // MultiDownloader.h
3 // MultiDownloaderDemo
4 //
5 // Created by hellovoidworld on 15/1/27.
6 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
7 //
8
9 #import "FileDownloader.h"
10
11 @interface MultiDownloader : FileDownloader
12
13 @end
14
15 //
16 // MultiDownloader.m
17 // MultiDownloaderDemo
18 //
19 // Created by hellovoidworld on 15/1/27.
20 // Copyright (c) 2015年 hellovoidworld. All rights reserved.
21 //
22
23 #import "MultiDownloader.h"
24 #import "SingleDownloader.h"
25
26 #define MaxMultilineCount 4
27
28 @interface MultiDownloader()
29
30 @property(nonatomic, strong) NSMutableArray *singleDownloaders;
31
32 @end
33
34 @implementation MultiDownloader
35
36 /** 初始化单线下载器 */
37 - (NSMutableArray *)singleDownloaders {
38 if (!_singleDownloaders) {
39 _singleDownloaders = [NSMutableArray array];
40
41 long long fileSize = [self getFileSize];
42 long long singleFileSize = 0; // 每条子线下载量
43 if (fileSize % MaxMultilineCount == 0) {
44 singleFileSize = fileSize / MaxMultilineCount;
45 } else {
46 singleFileSize = fileSize / MaxMultilineCount + 1;
47 }
48
49 for (int i=0; i<MaxMultilineCount; i++) {
50 SingleDownloader *downloader = [[SingleDownloader alloc] init];
51 downloader.url = self.url;
52 downloader.filePath = self.filePath;
53 downloader.begin = i * singleFileSize;
54 downloader.end = downloader.begin + singleFileSize - 1;
55 downloader.progressHandler = ^(double progress){
56 NSLog(@"%d号单线下载器正在下载,下载进度:%f", i, progress);
57 };
58
59 [self.singleDownloaders addObject:downloader];
60 }
61
62 // 创建临时文件,文件大小要跟实际大小一致
63 // 1.创建一个0字节文件
64 [[NSFileManager defaultManager] createFileAtPath:self.filePath contents:nil attributes:nil];
65
66 // 2.指定文件大小
67 NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
68 [fileHandle truncateFileAtOffset:fileSize];
69 }
70
71 return _singleDownloaders;
72 }
73
74 /** 获得文件大小 */
75 - (long long) getFileSize {
76 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
77 request.HTTPMethod = @"HEAD";// 请求得到头响应
78
79 NSURLResponse *response = nil;
80 [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
81 return response.expectedContentLength;
82 }
83
84 /** 开始下载 */
85 - (void)startDownloading {
86 [self.singleDownloaders makeObjectsPerformSelector:@selector(startDownloading)];
87 _downloading = YES;
88 NSLog(@"多线程下载开始");
89 }
90
91 /** 暂停下载 */
92 - (void)pauseDownloading {
93 [self.singleDownloaders makeObjectsPerformSelector:@selector(pauseDownloading)];
94 _downloading = NO;
95 NSLog(@"多线程下载暂停!");
96 }
97
98 @end
99
100 (4)控制器调用
101 - (IBAction)startDownloading {
102 MultiDownloader *downloader = [[MultiDownloader alloc] init];
103 downloader.url = @"http://192.168.0.21:8080/MyTestServer/videos/0627.zip";
104
105 // 设置存放路径
106 NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
107 downloader.filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
108
109 // 开始下载
110 [downloader startDownloading];
111 }