之前自己单独做一个项目的时候第一次接触文件下载的断点续传功能,当时查找了很多博客和资料,由于第一次接触,使用了别人封装的一个工具类来做的,开发过程中发现会有一些问题,因为发生的几率不大,因此没有过多优化。当时找的工具类是基于AFNetworking写的,继承的AFHTTPRequestOperation类做的封装,提供了暂停喝继续的接口,也对父类的一些方法做了重写,当时就拿来用了,后来发现频繁点击暂停继续会使文件的游标出现错误,修改了一些类中的一些设置也只是降低触发的几率。
最近又需要做一个下载断点续传的功能,刚开始把之前的办法搬过来了,使用后发现还是会出现上述问题,于是上网再次开始查找资料,才发现AFHTTPRequestOperation本身对下载已经做好了任务,只需要在外部逻辑上修改自己需要的下载/暂停/继续/取消的逻辑就可以了,这里感谢http://blog.sina.com.cn/s/blog_a5d3d4490102w05m.html博主的博客。
下面是断点续传的代码,这里配合sqlite数据库进行下载进度及下载状态的读写。(代码仅供参考,因人懒只写了一些标注- -,也可以参考上面博客的内容)
-(void)controlModelWithModel:(DownloadModel *)download_model andType:(NSString *)type{ NSString *md5 = download_model.model_md5_str; //下载 DownloadModel *model; BOOL hasModel = NO; DownloadListTable *table = (DownloadListTable *)[[DataBaseUtil shareInstance]getTableWithClass:[DownloadListTable class]]; for (DownloadModel *m in self.modelArr) { if ([m.model_md5_str isEqualToString:md5]) { model = m; hasModel = YES; } } if (!hasModel) { //列表中没有找到对应的模型 NSLog(@"error : not find model in arr"); return; } model.model_download_flag = @"1"; if (!model.model_download_percentage||[model.model_download_percentage isEqualToString:@"null"]) { model.model_download_percentage = @"0.00"; } //插入数据库表,如果存在则更新 [table insertTableWithConfition:model]; AppDelegate *app = [UIApplication sharedApplication].delegate; //读取全局保存的operation AFHTTPRequestOperation *operation = [app.downloadDic objectForKey:md5]; NSString *name = [NSString stringWithFormat:@"%@.%@",model.model_md5_str,model.model_format]; NSString *toPath = SAVE_MODEL_PATH(name); NSFileManager *fm = [NSFileManager defaultManager]; unsigned long long downloadedBytes = 0; //判断operation是否存在,不存在则创建 if (!operation) { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:model.model_url]]; if ([fm fileExistsAtPath:toPath])//如果存在,说明有缓存文件 { downloadedBytes = [self fileSizeAtPath:toPath];//计算缓存文件的大小 NSMutableURLRequest *mutableURLRequest = [request mutableCopy]; NSString *requestRange = [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes]; [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"]; request = mutableURLRequest; NSLog(@"==============断点下载"); } operation= [[AFHTTPRequestOperation alloc] initWithRequest:request]; [operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:toPath append:YES]]; [app.downloadDic setObject:operation forKey:md5]; } //设置进度操作 [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { float progress = ((float)totalBytesRead + downloadedBytes) / (totalBytesExpectedToRead + downloadedBytes); NSString *text = [NSString stringWithFormat:@"%.1f/%.1fM",((float)totalBytesRead + downloadedBytes)/1000/1000,(totalBytesExpectedToRead + downloadedBytes)/1000/1000.0]; NSLog(@"================downloadPrecent:%.2f size:%@",progress*100,text); //将进度保存到数据库文件中 if (![[DataBaseUtil shareInstance]isDataBaseInUse]) { model.model_download_percentage = [NSString stringWithFormat:@"%.2f",progress*100]; [table updateTableWithConfition:model]; } }]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){ //计算/匹配 md5串 NSString *file_md5_str = [TRMD5 file_md5:toPath]; BOOL isRight = NO; if ([file_md5_str isEqualToString:md5]) { isRight = YES; } //如果md5匹配,则将数据保存,数据库修改为3下载完成 //如果不匹配,删掉下载文件,数据库删除该条数据 if (isRight) { //md5匹配成功,完成下载,下载缩略图并修改数据库信息 [self modelDownloadFinishedWithModel:model]; }else{ //md5匹配失败,删除文件 [table deleteTableWithConfition:model]; [fm removeItemAtPath:toPath error:nil]; } NSLog(@"downloadFinished"); } failure:^(AFHTTPRequestOperation *operation, NSError *error){ //下载失败的话删除文件,数据路信息 if (error.code == NSURLErrorCancelled || error.code == NSURLErrorNetworkConnectionLost) { //如果取消或网络连接断开,则不做处理,设置为暂停 }else{ [table deleteTableWithConfition:model]; [fm removeItemAtPath:toPath error:nil]; NSLog(@"downloadFail,errorCode:%@",error); } }]; //开始执行操作 switch ([type intValue]) { case 0://第一次下载 [operation start]; model.model_download_flag = @"1"; [table updateTableWithConfition:model]; break; case 1://暂停 [operation cancel]; operation = nil; [app.downloadDic removeObjectForKey:md5]; model.model_download_flag = @"2"; [table updateTableWithConfition:model]; break; case 2://继续下载 [operation start]; model.model_download_flag = @"1"; [table updateTableWithConfition:model]; break; case 3://下载完成的模型 break; default: break; } } //获取文件大小 -(unsigned long long)fileSizeAtPath:(NSString *)path{ signed long long fileSize = 0; NSFileManager *fileManager = [NSFileManager new]; if ([fileManager fileExistsAtPath:path]) { NSError *error = nil; NSDictionary *fileDict = [fileManager attributesOfItemAtPath:path error:&error]; if (!error && fileDict) { fileSize = [fileDict fileSize]; } }else{ fileSize = 0; } return fileSize; }