#define kReceiveTotal @"receiveTotal"
#define kTotal @"total"
@interface ViewController ()
@end
/**
一、下载的性能优化
1. 使用一个缓冲Data存储下载的数据,当缓冲的数据>500kb,将此缓冲数据写入文件,并且清除缓冲数据,以节约内存
二、断点续传的实现思路:
1. 暂停的时候,记录下载的暂停位置
2. 继续下载时,设置下载的起始位置
http协议的请求头的设置:
Range : bytes=0-499 表示头500个字节
Range : bytes=500-999 表示第二个500字节
Range : bytes=-500 表示最后500个字节
Range : bytes=500- 表示500字节以后的范围
Range : bytes=0-0,-1 第一个和最后一个字节
3. 续传写入文件时,总是从文件末尾追加写入文件
*/
//缓冲目录 1.png
//目标目录 1.png
@implementation ViewController {
//2.下载数据的总大小
double total;
//3.接受数据的大小
double receiveTotal;
//4.缓冲数据
NSMutableData *_bufferData;
//5.下载路径(缓冲目录)
NSString *_filePath;
NSURLConnection *_connection;
}
- (void)viewDidLoad {
[super viewDidLoad];
//取得下载大小的数据
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
receiveTotal = [[userDefaults objectForKey:kReceiveTotal] doubleValue];
total = [[userDefaults objectForKey:kTotal] doubleValue];
//显示上次下载的进度
if (total > 0) {
CGFloat progress = receiveTotal/total;
_progressView.progress = progress;
_progreeLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];
}
}
- (IBAction)startAction:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://img31.mtime.cn/pi/2013/03/13/115558.26463213_1280X720.jpg"];
//默认就是GET请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置不缓存
[request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
#warning 断点续传添加的代码
if (receiveTotal > 0) {
//设置继续下载的位置
NSString *value = [NSString stringWithFormat:@"bytes=%d-",(int)receiveTotal];
[request setValue:value forHTTPHeaderField:@"Range"];
NSLog(@"继续下载:%@",request.allHTTPHeaderFields);
}
//发送异步请求
_connection = [NSURLConnection connectionWithRequest:request delegate:self];
//创建文件
//取得字符串的URL
NSString *urlstring = url.absoluteString;
//取得URL的最后一部分
NSString *filename = [urlstring lastPathComponent];
//文件路径
_filePath = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/temp/%@",filename];
//判断文件是否存在,如果不存在,再创建
if (![[NSFileManager defaultManager] fileExistsAtPath:_filePath]) {
//1.创建文件夹
NSString *dirPath = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/temp"];;
//2.创建文件
[[NSFileManager defaultManager] createFileAtPath:_filePath contents:nil attributes:nil];
}
}
//暂停下载
- (IBAction)pauseAction:(id)sender {
//1.取消下载连接
[_connection cancel];
_connection = nil;
//2.将缓冲数据写入文件
[self appendFileData:_bufferData];
//3.本地保存下载的位置
#warning 后续在添加
NSUserDefaults *userDetaults = [NSUserDefaults standardUserDefaults];
//保存已接受的大小
[userDetaults setObject:@(receiveTotal) forKey:kReceiveTotal];
[userDetaults setObject:@(total) forKey:kTotal];
//将数据同步写入文件
[userDetaults synchronize];
}
#pragma mark - NSURLConnection delegate
//1.服务响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_bufferData = [[NSMutableData alloc] init];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
//判断总大小是否为0,如果是0,说明从起始位置下载,需要获取文件大总大小
if (total == 0) {
//取得所有的响应头
NSDictionary *fields = [httpResponse allHeaderFields];
//取得数据的总大小
NSNumber *length = [fields objectForKey:@"Content-Length"];
total = [length doubleValue];
}
NSLog(@"%@",httpResponse.allHeaderFields);
}
//2.数据的传输
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//1.接受传输的数据
[_bufferData appendData:data];
//2.接受的数据大小/总大小 = 进度
receiveTotal += data.length;
double progress = receiveTotal / total;
//3.刷新UI
_progressView.progress = progress;
_progreeLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];
// NSLog(@"接受了%d字节数据",data.length);
NSLog(@"已接受了%d字节数据",(int)receiveTotal);
//4.判断缓冲的数据大小是否>10kb
if (_bufferData.length > 10*1000) {
[self appendFileData:_bufferData];
[_bufferData setData:nil];
}
}
//3.数据传输完成
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//将最后一个缓冲区数据写入文件
if (_bufferData.length < 10*1000) {
[self appendFileData:_bufferData];
[_bufferData setData:nil];
}
_progreeLabel.text = @"下载完成";
//将下载完成的文件从缓冲目录剪切到目标目录
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fileName = [_filePath lastPathComponent];
NSString *targetFile = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/%@",fileName];
//判断目标文件是否存在,如果存在,就删除掉
if ([fileManager fileExistsAtPath:targetFile]) {
[fileManager removeItemAtPath:targetFile error:nil];
}
//剪切文件,注意:剪切不能将已存在的文件覆盖
[fileManager moveItemAtPath:_filePath toPath:targetFile error:nil];
//下载完成之后,将本地保存的下载位置清零
NSUserDefaults *userDefatuls = [NSUserDefaults standardUserDefaults];
[userDefatuls setObject:@(0) forKey:kReceiveTotal];
[userDefatuls setObject:@(0) forKey:kTotal];
//将数据同步写入文件
[userDefatuls synchronize];
receiveTotal = 0;
total = 0;
}
//将数据追加到文件末尾
- (void)appendFileData:(NSData *)appData {
if (appData.length == 0 || _filePath.length == 0) {
return;
}
// 使用NSFileHandle写文件,此文件必须已经创建,NSFileHandle是不会创建文件的
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:_filePath];
//将文件的写入点,定位到文件的末尾
[fileHandle seekToEndOfFile];
[fileHandle writeData:appData];
[fileHandle closeFile];
}
@end