1 #import <Foundation/Foundation.h>
2
3 @interface HMFileDownloader : NSObject
4 /**
5 * 所需要下载文件的远程URL(连接服务器的路径)
6 */
7 @property (nonatomic, copy) NSString *url;
8 /**
9 * 文件的存储路径(文件下载到什么地方)
10 */
11 @property (nonatomic, copy) NSString *destPath;
12
13 /**
14 * 是否正在下载(有没有在下载, 只有下载器内部才知道)
15 */
16 @property (nonatomic, readonly, getter = isDownloading) BOOL downloading;
17
18 /**
19 * 用来监听下载进度
20 */
21 @property (nonatomic, copy) void (^progressHandler)(double progress);
22 /**
23 * 用来监听下载完毕
24 */
25 @property (nonatomic, copy) void (^completionHandler)();
26 /**
27 * 用来监听下载失败
28 */
29 @property (nonatomic, copy) void (^failureHandler)(NSError *error);
30
31 /**
32 * 开始(恢复)下载
33 */
34 - (void)start;
35
36 /**
37 * 暂停下载
38 */
39 - (void)pause;
40
41 @end
42
43
44
45 #import "HMFileDownloader.h"
46
47 @interface HMFileDownloader() <NSURLConnectionDataDelegate>
48 /**
49 * 连接对象
50 */
51 @property (nonatomic, strong) NSURLConnection *conn;
52
53 /**
54 * 写数据的文件句柄
55 */
56 @property (nonatomic, strong) NSFileHandle *writeHandle;
57 /**
58 * 当前已下载数据的长度
59 */
60 @property (nonatomic, assign) long long currentLength;
61 /**
62 * 完整文件的总长度
63 */
64 @property (nonatomic, assign) long long totalLength;
65 @end
66
67 @implementation HMFileDownloader
68
69 /**
70 * 开始(恢复)下载
71 */
72 - (void)start
73 {
74 NSURL *url = [NSURL URLWithString:self.url];
75 // 默认就是GET请求
76 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
77 // 设置请求头信息
78 NSString *value = [NSString stringWithFormat:@"bytes=%lld-", self.currentLength];
79 [request setValue:value forHTTPHeaderField:@"Range"];
80 self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
81
82 _downloading = YES;
83 }
84
85 /**
86 * 暂停下载
87 */
88 - (void)pause
89 {
90 [self.conn cancel];
91 self.conn = nil;
92
93 _downloading = NO;
94 }
95
96 #pragma mark - NSURLConnectionDataDelegate 代理方法
97 /**
98 * 1. 当接受到服务器的响应(连通了服务器)就会调用
99 */
100 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
101 {
102 #warning 一定要判断
103 if (self.totalLength) return;
104
105 // 1.创建一个空的文件到沙盒中
106 NSFileManager *mgr = [NSFileManager defaultManager];
107 // 刚创建完毕的大小是0字节
108 [mgr createFileAtPath:self.destPath contents:nil attributes:nil];
109
110 // 2.创建写数据的文件句柄
111 self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
112
113 // 3.获得完整文件的长度
114 self.totalLength = response.expectedContentLength;
115 }
116
117 /**
118 * 2. 当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)
119 */
120 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
121 {
122 // 累加长度
123 self.currentLength += data.length;
124
125 // 显示进度
126 double progress = (double)self.currentLength / self.totalLength;
127 if (self.progressHandler) { // 传递进度值给block
128 self.progressHandler(progress);
129 }
130
131 // 移动到文件的尾部
132 [self.writeHandle seekToEndOfFile];
133 // 从当前移动的位置(文件尾部)开始写入数据
134 [self.writeHandle writeData:data];
135 }
136
137 /**
138 * 3. 当服务器的数据接受完毕后就会调用
139 */
140 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
141 {
142 // 清空属性值
143 self.currentLength = 0;
144 self.totalLength = 0;
145
146 if (self.currentLength == self.totalLength) {
147 // 关闭连接(不再输入数据到文件中)
148 [self.writeHandle closeFile];
149 self.writeHandle = nil;
150 }
151
152 if (self.completionHandler) {
153 self.completionHandler();
154 }
155 }
156
157 /**
158 * 请求错误(失败)的时候调用(请求超时\断网\没有网, 一般指客户端错误)
159 */
160 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
161 {
162 if (self.failureHandler) {
163 self.failureHandler(error);
164 }
165 }
166
167 @end
1 #import "HMViewController.h"
2 #import "HMFileDownloader.h"
3
4 @interface HMViewController ()
5 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
6 @property (nonatomic, strong) HMFileDownloader *fileDownloader;
7
8 - (IBAction)start:(UIButton *)button;
9 @end
10
11 @implementation HMViewController
12
13 - (HMFileDownloader *)fileDownloader
14 {
15 if (!_fileDownloader) {
16 _fileDownloader = [[HMFileDownloader alloc] init];
17 // 需要下载的文件远程URL
18 _fileDownloader.url = @"http://192.168.1.200:8080/MJServer/resources/jre.zip";
19 // 文件保存到什么地方
20 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
21 NSString *filepath = [caches stringByAppendingPathComponent:@"jre.zip"];
22 _fileDownloader.destPath = filepath;
23 // typeof(10) a = 20; // int a = 20;
24
25 __weak typeof(self) vc = self;
26 _fileDownloader.progressHandler = ^(double progress) {
27 vc.progressView.progress = progress;
28 };
29
30 _fileDownloader.completionHandler = ^{
31 NSLog(@"------下载完毕");
32 };
33
34 _fileDownloader.failureHandler = ^(NSError *error){
35
36 };
37 }
38 return _fileDownloader;
39 }
40
41 - (void)viewDidLoad
42 {
43 [super viewDidLoad];
44
45 }
46
47 // 按钮文字: "开始", "暂停"
48 - (IBAction)start:(UIButton *)button { // self.currentLength == 200
49 if (self.fileDownloader.isDownloading) { // 暂停下载
50 [self.fileDownloader pause];
51
52 [button setTitle:@"恢复" forState:UIControlStateNormal];
53 } else { // 开始下载
54 [self.fileDownloader start];
55
56 [button setTitle:@"暂停" forState:UIControlStateNormal];
57 }
58 }
59
60 @end