iOS开发网络多线程之断点下载

一. 断点下载文件
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];}

浙公网安备 33010602011771号