首先我们懒加载一下:NSURLSession
会话配置对象
- 大多情况使用默认的配置就可以了
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
- NSOperationQueue:没有串行的概念
- delegateQueue:传入nil和[[NSOperationQueue alloc] init]效果是一样的。并发执行代理方法
- delegateQueue:传人[NSOperationQueue mainQueue],在主线程执行代理方法,不会阻塞主线程处理其他事件。
- 如何选择队列
- 如果代理方法中要更新UI/不做耗时操作,则选择主队列,其他都选择自定义队列
- session会对代理进行强引用,直到app退出或者显示销毁会话对象
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
- (NSURLSession *)session
{
if (_session == nil)
{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
完成了懒加载。
写一个方法,根据文件url获得续传数据保存的路径
- (NSString *)resumeDataPath:(NSURL *)url{
// 获得保存续传数据的文件名字
NSString *fileName = [NSString stringWithFormat:@"%@~resume",url.absoluteString.md5String];
// 获得全路径
NSString *resumePath = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSLog(@"保存续传数据的路径 = %@",resumePath);
return resumePath;
}
根据url加载续传数据
/**
* 根据url加载续传数据
*/
- (NSData *)loadResumeData:(NSURL *)url{
// 获得续传数据保存的路径
NSString *filePath = [self resumeDataPath:url];
return [NSData dataWithContentsOfFile:filePath];
}
点击开始按钮
/**
* 开始下载
*/
- (IBAction)start {
NSURL *url = [NSURL URLWithString:@"http://localhost/abc.mp4"];
self.url = url;
// 从沙盒中加载续传数据
self.resumeData = [self loadResumeData:self.url];
if (self.resumeData == nil) { // 没有续传数据
// 使用代理,就不能使用全局session
// 一旦提供了 completionHandler 回调,代理方法不会被触发。
self.downloadTask = [self.session downloadTaskWithURL:url];
} else { // 有续传数据
// 根据续传数据发起下载任务,那么任务的下载就从续传数据指定的位置开始下载
self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
}
// 继续任务
[self.downloadTask resume];
}
暂停下载
/**
* 暂停下载
*/
- (IBAction)pause {
// resumeData:续传数据,本质一个plist文件
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
NSLog(@"暂停下载 = %zd",resumeData.length);
// 记录续传数据
self.resumeData = resumeData;
// 清空下载任务
self.downloadTask = nil;
// 将续传数据写入文件中
[resumeData writeToFile:[self resumeDataPath:self.url] atomically:YES];
}];
}
继续下载
- (IBAction)resume {
if (self.resumeData == nil) {
NSLog(@"没有续传数据");
return;
}
// 根据续传数据发起下载任务,那么任务的下载就从续传数据指定的位置开始下载
self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
// 继续任务
[self.downloadTask resume];
}
NSURLSessionDownloadDelegate 代理方法
/**
* 下载完成回调 -- 必须实现
*
* @param session 会话
* @param downloadTask 下载任务
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"下载完成 = %@",location);
// 当任务执行完成后销毁会话
[self.session finishTasksAndInvalidate];
self.session = nil;
}
进度可以在这个方法中得到:
/**
* @param bytesWritten 本次下载的字节数
* @param totalBytesWritten 已经下载的字节数
* @param totalBytesExpectedToWrite 文件总字节数
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
// 计算进度
CGFloat progress = (CGFloat)totalBytesWritten / totalBytesExpectedToWrite;
NSLog(@"%f",progress);
self.progressView.progress = progress;
}
这个方法,没什么用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
NSLog(@"%s",__FUNCTION__);
}
自此,就完成了文件的下载,做到了内存以及断点续传