[iOS 多线程 & 网络

A.需求
  1. 边下边写入硬盘
  2. 显示下载进度
  3. 暂停/恢复 下载
  4. 解压文件
  5. 多线程下载
 
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];
 
Image(42)
 
 
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得到文件大小,再累加每次接收到的数据,计算出完成百分比
Image(43)
 
 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.准备实验文件
Image(44)
 
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来实现文件下载部分的不同
 
Image(45)
 
(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  
1004)控制器调用
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 }
 
 
 
 
原文地址:https://www.cnblogs.com/hellovoidworld/p/4252556.html