首页 > 代码库 > iOS:多线程断点下载

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 *)fileMultiDownloader10 {11     if (!_fileMultiDownloader) {12         _fileMultiDownloader = [[YYFileMultiDownloader alloc] init];13         // 需要下载的文件远程URL14         _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)viewDidLoad24 {25     [super viewDidLoad];26     27 }28 29 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event30 {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 YYFileDownloader4 @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 = http://www.mamicode.com/[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 *)error102 {103     104 }105 106 @end

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

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

YYFileMultiDownloader.h文件

1 #import "YYFileDownloader.h"2 3 @interface YYFileMultiDownloader : YYFileDownloader4 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 @end10 11 @implementation YYFileMultiDownloader12 13 - (void)getFilesize14 {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 *)singleDownloaders25 {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.totalLengt57         NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];58         [handle truncateFileAtOffset:self.totalLength];59     }60     return _singleDownloaders;61 }62 63 /**64  * 开始(恢复)下载65  */66 - (void)start67 {68     [self.singleDownloaders makeObjectsPerformSelector:@selector(start)];69     70     _downloading = YES;71 }72 73 /**74  * 暂停下载75  */76 - (void)pause77 {78     [self.singleDownloaders makeObjectsPerformSelector:@selector(pause)];79     _downloading = NO;80 }81 82 @end

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

iOS:多线程断点下载