NSURLConnection实现断点续传
⾸先来了解下这个东⻄西: Range头域 Range头域可以请求实体的⼀一个或者多个⼦子范围。
例如, 表⽰示头500个字节:bytes=0-499
表⽰示第⼆二个500字节:bytes=500-999
表⽰示最后500个字节:bytes=-500
表⽰示500字节以后的范围:bytes=500-
第⼀一个和最后⼀一个字节:bytes=0-0,-1
同时指定⼏几个范围:bytes=500-600,601-999
实现断点下载就是在httpRequest中加⼊入 Range 头。
[request addValue:@"bytes=500-" forHTTPHeaderField:@"Range"];
⾄至于能否正常实现断点续传,还要看服务器是否⽀支持。 如果⽀支持,⼀一切没问题。 如果不⽀支持可能出现两种情况,1.不理会你得range值,每次都重
新下载数据;2.直接下载失败。
过程如下
1.下载要保存到本地沙盒中,所以首先要获取沙河中的 全路径,(文件保存在沙盒中Documents/%@)中,目的是获取以进攻下载的文件大小
MD5加密,是一款数据加密工具,为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。MD5hash,哈希值计算器,是一款md5校验工具。每个文件都可以用Hash MD5验证程序算出一个固定的MD5码来。
//获取在沙盒中的全路径
- (NSString *)getFullFilePathWithUrl:(NSString *)urlname {
NSString * name = [urlname MD5Hash];
//获取在沙盒中的路径
NSString *path = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/%@",name];
return path;
}
判断有没有这个文件 有就获取大小 没有就 创建这个文件
BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if (!isExist) {
self.loadedSize = 0;
//没有就创建新的空文件
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
}else {
//有 要获取已经下载的文件大小
NSDictionary *fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
self.loadedSize = fileDict.fileSize;
}
//打开文件
_fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
//发送下载请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
//断点下载 应该 告知 服务器 从哪一字节开始下载
//通过请求头 发给 服务器 (前提 服务器 必须要支持断点续传)
//增加请求头 Range头
[request addValue:[NSString stringWithFormat:@"bytes=%llu-",self.loadedSize] forHTTPHeaderField:@"Range"];
//启动一个线程 进行请求下载
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
遵守协议
#pragma mark - NSURLConnectionDataDelegate
//服务器给客户端响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
//服务器告知 客户端 要发数据了 还会告诉发送的大小(文件 剩余的大小)
NSLog(@"len:%llu",response.expectedContentLength);
//计算文件 总大小
self.fileSize = self.loadedSize+response.expectedContentLength;
}
//服务发送数据的时候 回调
//一段一段发送
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//下载过程 要把data 写到本地沙盒
[_fileHandle seekToEndOfFile];//定位到文件尾
[_fileHandle writeData:data];
[_fileHandle synchronizeFile];//同步到磁盘
//文件 已经下载大小
self.loadedSize += data.length;
//回调block 把 大小传给对方
if (self.myBlock) {
self.myBlock(self);************Block重点标记在这里传值***************
}
}
停止下载 关闭文件
- (void)stopDownload {
if (_connection) {
[_connection cancel];
_connection = nil;
}
[_fileHandle closeFile];//关闭文件
}
view 会负责界面的展示,考虑到界面的刷新 ,会会使用定时器,来控制界面的刷新,
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//获取比例
double scale = [[NSUserDefaults standardUserDefaults] doubleForKey:kUrl];
self.slider.value = scale;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//计算 速度 每隔1s 调用一次
- (void)getSpeed {
//当前的已经下载大小 - 前1s 已经下载大小
self.speed = (self.loadedSize-self.preLoadedSize)/1024.0;
//保存
self.preLoadedSize = self.loadedSize;
}
- (IBAction)startClick:(id)sender {
if (!self.timer) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(getSpeed) userInfo:nil repeats:YES];
}
__weak typeof(self)weakSelf = self;
[self.download breakPointDownloadDataWithUrl:kUrl downloadProcess:^(BreakPointDownload *download) {***********//block 传值的类型
//保存已经下载大小
weakSelf.loadedSize = download.loadedSize;
//下载过程中 会 回调 这个block*************************************
double fileSize = download.fileSize/1024.0/1024.0;//M
//使用Block传过来的值*****************************
double scale = ((double)download.loadedSize)/download.fileSize;
weakSelf.slider.value = scale;
//把这个进度 保存的本地
[[NSUserDefaults standardUserDefaults] setDouble:scale forKey:kUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
if (scale >= 1.0) {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
//销毁定时器
}
weakSelf.label.text = [NSString stringWithFormat:@"已经下载%.2f%% 总大小 %.2fM 速度 %.2fKB/S",scale*100,fileSize,weakSelf.speed];
}];
}
- (IBAction)stopClick:(id)sender {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
[self.download stopDownload];
}
总结
1.首先去沙盒中找到文件,打开文件。(_fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath])获取已经下载的文件大小
2.断点续传,要拼接请求 增加请求头Range 头,让服务器知道从哪里开始下载,所以叫断点续传
3.开始下载会调用协议方法,并开始通过Block回调,把数据返还给View,
4.View界面展示数据,通过调用下载方法(含有Block),下载的过程中通过Block返回的值给界面输出数据,
5.启用定时器,刷新界面,展示数据
6.停止下载时,首先使定时器销毁,然后 使NSURLConnection取消请求链接([_connection cancel])关闭文件