hey,I refer your idea to write a download manager, but does not support breakpoints download, hope you can help me analyze, be grateful.
#import "DownLoadViewController.h"
#import "BaseCell.h"
#import <AFNetworking.h>
#import "DownModel.h"
#define fileDest [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/"]
NSString * const kMZDownloadKeyURL = @"URL";
NSString * const kMZDownloadKeyStartTime = @"startTime";
NSString * const kMZDownloadKeyFileName = @"fileName";
NSString * const kMZDownloadKeyProgress = @"progress";
NSString * const kMZDownloadKeyTask = @"downloadTask";
NSString * const kMZDownloadKeyStatus = @"requestStatus";
NSString * const kMZDownloadKeyDetails = @"downloadDetails";
NSString * const kMZDownloadKeyResumeData = @"resumedata";
NSString * const RequestStatusDownloading = @"RequestStatusDownloading";
NSString * const RequestStatusPaused = @"RequestStatusPaused";
NSString * const RequestStatusFailed = @"RequestStatusFailed";
@interface DownLoadViewController ()<UITableViewDataSource,UITableViewDelegate,NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDownloadDelegate,NSURLSessionDataDelegate>
{
UITableView *mainTable;
NSMutableArray *tableData;
/** 保存所有下载信息的数组 */
NSMutableArray *downloadingArray;
}
@end
@implementation DownLoadViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"视频下载";
mainTable = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
mainTable.delegate = self;
mainTable.dataSource = self;
[self.view addSubview:mainTable];
tableData = [NSMutableArray array];
downloadingArray = [NSMutableArray array];
/** 请求下载列表 */
[self requestData];
}
/**
* 请求数据
*/
- (void)requestData
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/plain", @"text/html", nil];
[manager GET:@"http://ac.ybjk.com/vod_v1.php?km=km3&m=sp" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary *resultDic = (NSMutableDictionary *)responseObject;
NSArray *array = resultDic[@"data"];
for (NSDictionary *dic in array)
{
DownModel *model = [[DownModel alloc] init];
model.name = dic[@"title"];
model.url = dic[@"url"];
[tableData addObject:model];
}
/** 保存所有下载任务信息数组 初始化*/
for (int i = 0 ; i < tableData.count ; i++)
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionary];
[downloadingArray addObject:tmpDic];
}
NSLog(@"所有下载任务信息 长度 %lu",(unsigned long)downloadingArray.count);
[mainTable reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"请求失败");
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellId = [NSString stringWithFormat:@"%ld%ld",(long)indexPath.section,(long)indexPath.row];
BaseCell *cell = (BaseCell *)[tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell)
{
cell = [[BaseCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
DownModel *model = [tableData objectAtIndex:indexPath.row];
cell.nameLabel.text = model.name;
cell.downBtn.tag = indexPath.row;
[cell.downBtn addTarget:self action:@selector(downBtnAction:) forControlEvents:UIControlEventTouchUpInside];
NSString *fileName = [NSString stringWithFormat:@"%@.mp4",model.name];
// 2. 生成沙盒的路径
NSArray *docs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [docs[0] stringByAppendingPathComponent:fileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
/** 如果文件存在 */
cell.downBtn.hidden = YES;
cell.sizeLabel.text = @"已下载至本地";
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 60.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 15.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 0.1f;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
/**
* 刷新选中cell
*
* @param cell cell
* @param indexPath 选中行数
*/
- (void)updateCell:(BaseCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *downloadInfoDict = [downloadingArray objectAtIndex:indexPath.row];
/** 文件名称 */
NSString *fileName = [downloadInfoDict objectForKey:kMZDownloadKeyFileName];
cell.nameLabel.text = fileName;
/** 文件大小 */
[cell.detailTextLabel setText:[downloadInfoDict objectForKey:kMZDownloadKeyDetails]];
/** 下载进度 */
[cell.progressView setProgress:[[downloadInfoDict objectForKey:kMZDownloadKeyProgress] floatValue]];
}
#pragma mark - 下载事件
- (void)downBtnAction:(UIButton *)btn
{
/** 获得当前操作行数 */
NSIndexPath *path = [NSIndexPath indexPathForRow:btn.tag inSection:0];
BaseCell *cell = (BaseCell *)[mainTable cellForRowAtIndexPath:path];
/** 获得当前操作行数对象 */
DownModel *model = [tableData objectAtIndex:btn.tag];
/** 下载的文件名称 */
NSString *fileName = model.name;
/** 下载的url */
NSString *fileURL = model.url;
if (!btn.selected)
{
NSLog(@"开始下载");
[btn setSelected:YES];
/** 获得当前操作行数 下载任务信息 */
NSMutableDictionary *downloadInfo = [downloadingArray objectAtIndex:path.row];
/** 获得当前操作行数 下载任务 */
NSURLSessionDownloadTask *downloadTask = [downloadInfo objectForKey:kMZDownloadKeyTask];
if (downloadTask)
{
/** 执行断点下载 */
NSData *resumeData = [[downloadInfo objectForKey:kMZDownloadKeyResumeData] dataUsingEncoding:NSUTF8StringEncoding];
downloadTask = [self.sessionManager downloadTaskWithResumeData:resumeData];
[downloadTask resume];
}
else
{
/** 添加下载任务 */
[self addDownloadTask:fileName fileURL:fileURL indexRow:btn.tag];
}
}
else
{
NSLog(@"暂停下载");
[btn setSelected:NO];
/** 获得当前操作行数 下载任务信息 */
NSMutableDictionary *downloadInfo = [downloadingArray objectAtIndex:path.row];
/** 获得当前操作行数 下载任务 */
NSURLSessionDownloadTask *downloadTask = [downloadInfo objectForKey:kMZDownloadKeyTask];
/** 获得当前操作行数 下载状态 */
NSString *downloadStatus = [downloadInfo objectForKey:kMZDownloadKeyStatus];
if ([downloadStatus isEqualToString:RequestStatusDownloading])
{
/** 当前状态为正在下载,则暂停下载 */
// [downloadTask suspend]; /** 暂停下载 */
// [downloadTask cancel];
/** 断点下载,保存断点data */
[downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
NSString *dataString = [[NSString alloc] initWithData:resumeData encoding:NSUTF8StringEncoding];
NSLog(@"断点数据 %@",dataString);
[downloadInfo setObject:dataString forKey:kMZDownloadKeyResumeData];
}];
[downloadInfo setObject:RequestStatusPaused forKey:kMZDownloadKeyStatus]; /** 设置下载任务信息为暂停状态 */
/** 更新下载任务数组中 当前任务信息 */
[downloadingArray replaceObjectAtIndex:path.row withObject:downloadInfo];
/** 刷新 当前 选中 cell */
[self updateCell:cell forRowAtIndexPath:path];
}
}
}
#pragma mark - 懒加载 获得session
- (NSURLSession *)sessionManager
{
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
});
return session;
}
/**
* 添加下载任务
*
* @param fileName 文件名
* @param fileURL 文件URL
*/
- (void)addDownloadTask:(NSString *)fileName fileURL:(NSString *)fileURL indexRow:(NSInteger)indexRow
{
/** 初始化下载任务 */
NSURL *url = [NSURL URLWithString:fileURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDownloadTask *downloadTask = [self.sessionManager downloadTaskWithRequest:request];
/** 启动下载任务 */
[downloadTask resume];
/** 保存本次下载任务信息 */
NSMutableDictionary *downloadInfo = [NSMutableDictionary dictionary];
[downloadInfo setObject:fileURL forKey:kMZDownloadKeyURL]; /** 下载url */
[downloadInfo setObject:fileName forKey:kMZDownloadKeyFileName]; /** 下载文件名称 */
/** 将保存任务信息的字典转化为json字符串,设置本次下载任务的描述 */
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:downloadInfo options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
downloadTask.taskDescription = jsonString;
/** 继续向字典中添加本次下载任务信息 */
[downloadInfo setObject:RequestStatusDownloading forKey:kMZDownloadKeyStatus]; /** 下载状态 (正在下载,暂停下载,下载失败) */
[downloadInfo setObject:downloadTask forKey:kMZDownloadKeyTask]; /** 本次下载任务 */
/** 将本次下载任务信息保存到数组中 */
// [downloadingArray addObject:downloadInfo];
[downloadingArray replaceObjectAtIndex:indexRow withObject:downloadInfo];
}
#pragma mark - NSURLSession Delegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
/** 遍历下载任务信息数组,获得每一个下载任务信息 */
for (NSMutableDictionary *downloadDict in downloadingArray)
{
/** 获得当前所有下载任务中 与 代理方法中的 downloadTask 一致的 */
if ([downloadTask isEqual:[downloadDict objectForKey:kMZDownloadKeyTask]])
{
dispatch_async(dispatch_get_main_queue(), ^{
/** 回到主线程,渲染UI */
float progress = (double)downloadTask.countOfBytesReceived/(double)downloadTask.countOfBytesExpectedToReceive;
/** 获取当前任务所在cell */
NSInteger indexOfDownloadDict = [downloadingArray indexOfObject:downloadDict];
NSIndexPath *indexPathToRefresh = [NSIndexPath indexPathForRow:indexOfDownloadDict inSection:0];
BaseCell *cell = (BaseCell *)[mainTable cellForRowAtIndexPath:indexPathToRefresh];
/** 进度条 */
cell.progressView.progress = progress;
/** 文件大小 */
cell.sizeLabel.text = [NSString stringWithFormat:@"%.2fM/%.2fM",(CGFloat)totalBytesWritten/1024/1024,(CGFloat)totalBytesExpectedToWrite/1024/1024];
/** 保存当前下载任务的进度 以及 文件大小信息 */
[downloadDict setObject:[NSString stringWithFormat:@"%f",progress] forKey:kMZDownloadKeyProgress]; /** 保存进度 */
[downloadDict setObject:cell.sizeLabel.text forKey:kMZDownloadKeyDetails]; /** 文件大小 */
});
break;
}
}
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
/** 遍历下载任务信息数组,获得每一个下载任务信息 */
for (NSMutableDictionary *downloadDict in downloadingArray)
{
/** 获得当前所有下载任务中 与 代理方法中的 downloadTask 一致的 */
if ([downloadTask isEqual:[downloadDict objectForKey:kMZDownloadKeyTask]])
{
/** 保存的文件名 */
NSString *fileName = [downloadDict objectForKey:kMZDownloadKeyFileName];
/** 保存路径 */
NSString *savePath = [fileDest stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:savePath];
NSLog(@"下载文件保存路径: %@",savePath);
/** 将下载好的文件从临时文件夹中保存到指定路径 */
if (location)
{
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
if (error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:error.localizedDescription delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
}
break;
}
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"error %@",error.localizedDescription);
NSLog(@"===== %@",[error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData]);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}