一. 断点下载文件
1. 实现功能
点击"开始下载" -> 开始下载文件,进度条实时变化
点击"暂停下载" -> 暂停下载,进度条停止走动
点击"恢复下载" -> 接着上一次下载进度下载
将正在下载或在暂停没有下载完时,将APP关闭,在重新打开时,进度条现在之前的下载进度,点击开始下载,接着上次下载
2. 思路
1> 要实现断点下载,需要将已下载文件存放到cache缓存中,且把文件总大小存放到cache缓存中,程序每次启动,获取沙盒中已下载文件的大小和文件中大小,拿出这两个值之后就可以设置进度条的进度
2> 点击开始按钮:
- 创建一个NSURLSession会话对象,设置代理
- 创建一个NSURLSessionDataTask任务发送请求,创建请求时设置请求头部信息,声明文件从哪里开始下载
3> 实现代理方法
- 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中
- 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中
- 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil
3. 实现代码
1> 点击开始,开启任务
// 开始下载- (IBAction)start { [self.dataTask resume]; }
2> 点击暂停,暂停下载
// 暂停下载- (IBAction)suspond { [self.dataTask suspend];}
3> 点击恢复,恢复下载
// 恢复下载- (IBAction)recover { [self.dataTask resume]; }
4> 创建session会话,并设置代理
- (NSURLSession *)session{if (_session == nil) { // 1.创建session会话 _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];}return _session;}
5> 创建NSURLSessionDataTask任务
请求头部信息中声明从哪里开始下载
- (NSURLSessionDataTask *)dataTask{if (_dataTask == nil) { // 下载完成直接返回,不发送网络请求NSLog(@"curSize = %zd --- %zd", self.curSize, [self getTotalSize]);if (self.curSize == [self getTotalSize] && self.curSize != 0) {NSLog(@"下载完成");return nil;} // 2.创建dataTask任务/* 表示头500个字节:Range: bytes=0-499 表示第二个500字节:Range: bytes=500-999 表示最后500个字节:Range: bytes=-500 表示500字节以后的范围:Range: bytes=500- */ NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_04.mp4"];// 创建请求NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.curSize]; [request setValue:range forHTTPHeaderField:@"Range"]; _dataTask = [self.session dataTaskWithRequest:request];}return _dataTask;}
6> 代理方法实现
- 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{ // 如何处理服务器返回的数据 completionHandler(NSURLSessionResponseAllow); self.totalSize = response.expectedContentLength + self.curSize; // 将文件数据的大小保存到沙盒中// 取出沙盒中保存数据大小的字典NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize]; NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];if (dictM == nil) { // 如果没有初始化字典 dictM = [NSMutableDictionary dictionary];} // 给字典赋值 dictM[KfileName] = @(self.totalSize); // 再将字典回写到沙盒中[dictM writeToFile:totalSizePath atomically:YES]; // 拼接沙盒路径NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName]; self.filePath = filePath; // 创建输出流NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:filePath append:YES]; self.stream = stream;[stream open];}
- 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{NSLog(@"didReceiveData"); NSLog(@"%zd", data.length); // 已下载数据的大小 self.curSize += data.length; // 下载进度 self.progressView.progress = 1.0 * self.curSize / self.totalSize; self.label.text = [NSString stringWithFormat:@"%f", 1.0 * self.curSize / self.totalSize]; // 数据存放到本地[self.stream write:data.bytes maxLength:data.length]; }
- 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{NSLog(@"didCompleteWithError");[self.stream close]; self.stream = nil;}
7> 程序关闭在打开现在当前进度
- (void)viewDidLoad {[super viewDidLoad]; // 1.获取沙盒中已下载文件的大小NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName];NSLog(@"%@", filePath);NSFileManager *manager = [NSFileManager defaultManager]; NSDictionary *fileDict = [manager attributesOfItemAtPath:filePath error:nil]; self.curSize = [fileDict[@"NSFileSize"] integerValue];NSLog(@"%zd", self.curSize); // 2.获取沙盒中数据总大小 self.totalSize = [self getTotalSize]; if (self.totalSize == 0) {NSLog(@"第一次下载");} else { self.progressView.progress = 1.0 * self.curSize / self.totalSize;}}
获取沙盒中文件总大小
// 获取沙盒中文件总大小- (NSInteger)getTotalSize{NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize];NSLog(@"%@", totalSizePath);NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];NSLog(@"%@", dictM);if (dictM == nil) {return 0;} else {NSLog(@"------%zd", [dictM[KfileName] integerValue]);return [dictM[KfileName] integerValue]; } }
8> 凡创建的会话都需要在dealloc中释放内存
- (void)dealloc{// 会话释放[self.session invalidateAndCancel];}