1.文件下载类(父类)
//
// FileDownloader.h
// 文件下载器
// 一个FileDownloader下载一个文件
#import <Foundation/Foundation.h>
@interface FileDownloader : NSObject
{
BOOL _downloading;
}
/**
* 所需要下载文件的远程URL(连接服务器的路径)
*/
@property (nonatomic, copy) NSString *url;
/**
* 文件的存储路径(文件下载到什么地方)
*/
@property (nonatomic, copy) NSString *destPath;
/**
* 是否正在下载(有没有在下载, 只有下载器内部才知道)
*/
@property (nonatomic, readonly, getter = isDownloading) BOOL downloading;
/**
* 用来监听下载进度
*/
@property (nonatomic, copy) void (^progressHandler)(double progress);
/**
* 开始(恢复)下载
*/
- (void)start;
/**
* 暂停下载
*/
- (void)pause;
@end
//
// FileDownloader.m
// 文件下载器
//
#import "FileDownloader.h"
@implementation FileDownloader
@end
2.单点下载类
//
// FileSingleDownloader.h
#import "FileDownloader.h"
@interface FileSingleDownloader : FileDownloader
/**
* 下载的范围(开始位置)
*/
@property(nonatomic,assign)long long begin;
/**
* 下载的范围(结束位置)
*/
@property(nonatomic,assign)long long end;
@end
//
// FileSingleDownloader.m
#import "FileSingleDownloader.h"
@interface FileSingleDownloader() <NSURLConnectionDataDelegate>
/**
* 连接对象
*/
@property(nonatomic,strong)NSURLConnection *conn;
/**
* 写数据的文件句柄
*/
@property(nonatomic,strong)NSFileHandle *writeHandle;
/**
* 当前已下载的数据的长度
*/
@property(nonatomic,assign)long long currentLength;
@end
@implementation FileSingleDownloader
- (NSFileHandle *)writeHandle
{
if (!_writeHandle) {
_writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
}
return _writeHandle;
}
/**
* 开始(恢复)下载
*/
- (void)start
{
NSURL *url = [NSURL URLWithString:self.url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求头
NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld",self.begin+self.currentLength,self.end];
[request setValue:value forHTTPHeaderField:@"Range"];
self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
_downloading = YES;
}
/**
* 暂停
*/
- (void)pause
{
[self.conn cancel];
self.conn = nil;
_downloading = NO;
}
#pragma mark - NSURLConnectionDataDelegate
/**
* 1.当接受到服务器的响应的就会调用
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{}
/**
* 2.当接受到服务器返回的数据就会调用(可能会被调用多次,每次只会传递部分数据)
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//移到到文件的尾部
[self.writeHandle seekToFileOffset:self.begin + self.currentLength];
//从当前移动的位置(文件尾部)开始写入数据
[self.writeHandle writeData:data];
//2.累计已下载的数据的长度
self.currentLength += data.length;
//打印下载进度
double progress = (double)self.currentLength / (self.end-self.begin);
if (self.progressHandler) {
self.progressHandler(progress);
}
}
/**
* 3.当服务器的数据接受完毕后就会调用
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//清空属性值
self.currentLength = 0;
//关闭连接(不再输入数据到文件中)
[self.writeHandle closeFile];
self.writeHandle = nil;
}
/**
* 请求失败的时候调用(请求超时、断网、没有网。一般都是客户端错误)
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{}
@end
3.多点下载
//
// FileMultiDownloader.h
// 文件下载
// 多线程下载器(利用FileMultiDownloader能开启多个线程同时下载一个文件)
// 一个多线程下载器 只 下载一个文件
#import "FileDownloader.h"
@interface FileMultiDownloader : FileDownloader
@end
//
// FileMultiDownloader.m
#import "FileMultiDownloader.h"
#import "FileSingleDownloader.h"
#define MaxDownloadCount 3
@interface FileMultiDownloader()
@property(nonatomic,strong)NSMutableArray *singleDownloaders;
@property(nonatomic,assign)long long totalLength;
@end
@implementation FileMultiDownloader
/**
* 获得要下载的文件的大小
*/
- (void)getFilesize
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
request.HTTPMethod = @"HEAD";
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
self.totalLength = response.expectedContentLength;
}
- (NSMutableArray *)singleDownloaders
{
if (!_singleDownloaders) {
_singleDownloaders = [NSMutableArray array];
//获取文件大小
[self getFilesize];
//每条线程的下载量
long long size = 0;
if (self.totalLength % MaxDownloadCount == 0) {
size = self.totalLength / MaxDownloadCount;
}else{
size = self.totalLength / MaxDownloadCount + 1;
}
//创建N个下载器
for (int i=0; i<MaxDownloadCount; i++) {
FileSingleDownloader *singleDownloader = [[FileSingleDownloader alloc] init];
singleDownloader.url = self.url;
singleDownloader.destPath = self.destPath;
singleDownloader.begin = i * size;
singleDownloader.end = singleDownloader.begin + size - 1;
singleDownloader.progressHandler = ^(double progress){
//打印下载进度
// NSLog(@"%d----%f",i,progress);
};
[_singleDownloaders addObject:singleDownloader];
}
//创建一个跟服务器文件等大小的临时文件
[[NSFileManager defaultManager] createFileAtPath:self.destPath contents:nil attributes:nil];
//让self.destPath文件的长度是 self.totalLength
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
[handle truncateFileAtOffset:self.totalLength];
}
return _singleDownloaders;
}
/**
* 开始(恢复)下载
*/
- (void)start
{
[self.singleDownloaders makeObjectsPerformSelector:@selector(start)];
_downloading = YES;
}
/**
* 暂停
*/
- (void)pause
{
[self.singleDownloaders makeObjectsPerformSelector:@selector(pause)];
_downloading = NO;
}
@end
4.控制器调用
#import "ViewController.h"
#import "FileMultiDownloader.h"
@interface ViewController ()
//下载器
@property(nonatomic,strong)FileMultiDownloader *fileMultiDownloader;
@end
@implementation ViewController
- (FileMultiDownloader *)fileDownloader
{
if (!_fileMultiDownloader) {
_fileMultiDownloader = [[FileMultiDownloader alloc] init];
//需要下载的远程文件URL
_fileMultiDownloader.url = @"https://xxxx.com/images.zip";
//文件保存到什么地方
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
NSString *filepath = [caches stringByAppendingPathComponent:@"images.zip"];
_fileMultiDownloader.destPath = filepath;
}
return _fileMultiDownloader;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//开始下载
[self.fileMultiDownloader start];
}
@end