1.封装
//
// YKDFileDownloader.h
// 文件下载器
// 一个YKDFileDownloader下载一个文件
#import <Foundation/Foundation.h>
@interface YKDFileDownloader : NSObject
//所需要下载的文件的远程URL(https://xxx.com/1.zip)
@property(nonatomic,copy)NSString *url;
//下载文件的要保存的沙盒路径
@property(nonatomic,copy)NSString *destPath;
//是否正在下载
@property(nonatomic,readonly,getter=isDownloading)BOOL downloading;
//这个block可以用来监听下载进度
@property(nonatomic,copy)void (^progressHandler)(double progress);
//这个block可以用来监听下载完毕
@property(nonatomic,copy)void (^completionHandler)();
//这个block可以用来监听下载失败
@property(nonatomic,copy)void (^failureHandler)(NSError *error);
//开始(恢复)下载
- (void)start;
//暂停下载
- (void)pause;
@end
//
// YKDFileDownloader.m
// 文件下载器
//
#import "YKDFileDownloader.h"
@interface YKDFileDownloader()<NSURLConnectionDataDelegate>
//写数据的文件句柄
@property(nonatomic,strong)NSFileHandle *writeHandle;
//当前已下载的数据的长度
@property(nonatomic,assign)long long currentLength;
//完整文件的总长度
@property(nonatomic,assign)long long totalLength;
//连接对象
@property(nonatomic,strong)NSURLConnection *conn;
//是否正在下载
@property(nonatomic,assign,getter=isDownloading)BOOL downloading;
@end
@implementation YKDFileDownloader
/**
* 开始(恢复)下载
*/
- (void)start
{
NSURL *url = [NSURL URLWithString:@"https://localhost/resources/video.zip"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求头
NSString *value = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
[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
/**
* 当接受到服务器的响应的就会调用
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//文件下载完毕
if (self.totalLength) return;
//1.创建一个空的文件到沙盒中
NSFileManager *mgr = [NSFileManager defaultManager];
[mgr createFileAtPath:self.destPath contents:nil attributes:nil];
//2.创建写数据的文件句柄
self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
//3.获取完整文件的长度
self.totalLength = response.expectedContentLength;
}
/**
* 当接受到服务器返回的数据就会调用(可能会被调用多次,每次只会传递部分数据)
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//累计已下载的数据的长度
self.currentLength += data.length;
//显示进度
double progress = (double)self.currentLength / self.totalLength;
if (self.progressHandler) { //传递进度值
self.progressHandler(progress);
}
//移到到文件的尾部
[self.writeHandle seekToEndOfFile];
//从当前移动的位置开始写入数据
[self.writeHandle writeData:data];
}
/**
* 当服务器的数据接受完毕后就会调用
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//清空属性值
self.currentLength = 0;
self.totalLength = 0;
//关闭连接(不再输入数据到文件中)
[self.writeHandle closeFile];
self.writeHandle = nil;
if (self.completionHandler) {
self.completionHandler();
}
}
/**
* 请求失败的时候调用(请求超时、断网、没有网。一般都是客户端错误)
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (self.failureHandler) {
self.failureHandler(error);
}
}
@end
2.使用
//
// ViewController.m
// 大文件的下载
/*
如果文件比较小,下载方式会比较多
直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
利用NSURLConnection发送一个HTTP请求去下载
如果是下载图片,还可以利用SDWebImage框架
*/
#import "ViewController.h"
#import "YKDFileDownloader.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
- (IBAction)start:(UIButton*)button;
@property(nonatomic,strong)YKDFileDownloader *fileDownloader;
@end
@implementation ViewController
- (YKDFileDownloader *)fileDownloader
{
if (!_fileDownloader) {
self.fileDownloader = [[YKDFileDownloader alloc] init];
//需要下载的文件远程URL
_fileDownloader.url = @"https://localhost/resources/video.zip";
//文件保存到什么地方
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filepath = [caches stringByAppendingPathComponent:@"video.zip"];
_fileDownloader.destPath = filepath;
__weak typeof(self) vc = self;
_fileDownloader.progressHandler = ^(double progress){
// NSLog(@"%f",progress);
vc.progressView.progress = progress;
};
_fileDownloader.completionHandler = ^{
// NSLog(@"下载完毕");
};
_fileDownloader.failureHandler = ^(NSError *error){
NSLog(@"下载失败%@",error);
};
}
return _fileDownloader;
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)start:(UIButton *)button
{
if (self.fileDownloader.isDownloading) {
[self.fileDownloader pause];
[button setTitle:@"恢复" forState:UIControlStateNormal];
}else{
[self.fileDownloader start];
[button setTitle:@"暂停" forState:UIControlStateNormal];
}
}
@end