[iOS 多线程 & 网络 - 2.4] - 大文件下载 (边下边写/暂停恢复下载/压缩解压zip/多线程下载)

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 }

 

 
 
 
 

你可能感兴趣的:(多线程下载)