您的位置:首页 > 移动开发 > IOS开发

iOS边练边学--NSURLSessionDataTask实现文件真正的断点续传

2016-05-04 19:27 507 查看
实现重点:

NSURLSessionDataTask要设置请求头,从路径中获取文件已经下载的长度(文件没有下载过的话,长度为0)。通过这个长度设置请求的Range



如图:



接收到请求的时候key:文件名(经过MD5加密过的URL,Url保证了文件名的唯一) Value:该文件已经下载过的长度。保存成plist文件,方便对下载文件的判断

利用NSOutUpStream写文件

在任务完成的代理方法里面,NSOutUpStream关闭并且清空,对应的task清空,对应的session清空



代码如下:

#import "ViewController.h"
#import "NSString+Hash.h"

// 下载文件的URL
#define ChaosFileURL @"http://120.25.226.186:32812/resources/videos/minion_01.mp4"

// 根据文件唯一的URL MD5值 作为文件名
#define ChaosFileName ChaosFileURL.md5String

// 用来存储文件总长度的plist文件 key:文件名的MD5值 value:文件总长度
#define ChaosDownloadFilesPlist [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"downloadFiles.plist"]

// 下载文件的全路径
#define ChaosFileFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:ChaosFileName]

// 已经下载的文件长度
#define ChaosDownloadLength [[[NSFileManager defaultManager] attributesOfItemAtPath:ChaosFileFullPath error:nil][@"NSFileSize"] integerValue]

@interface ViewController () <NSURLSessionDataDelegate>

/** stream */
@property(nonatomic,strong) NSOutputStream *stream;

/** session */
@property(nonatomic,strong) NSURLSession *session;

/** task */
@property(nonatomic,strong) NSURLSessionDataTask *task;

/** totalLength */
@property(nonatomic,assign) NSInteger totalLength;

/** downloadLength */
@property(nonatomic,assign) NSInteger downloadLength;

@end

@implementation ViewController

- (NSURLSession *)session
{
if (_session == nil) {

_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
}

return _session;
}

- (NSURLSessionDataTask *)task
{
if (_task == nil) {

// 获得文件总长度
NSInteger totalLength = [[NSDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist][ChaosFileName] integerValue];
// 请求同一个文件,判断下载文件长度;如果没下载过此文件,totalLength = 0
if (totalLength && ChaosDownloadLength == totalLength) {
NSLog(@"文件已经下载过.");
return nil;
}

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ChaosFileURL]];

// 设置请求头 -- range 这次从哪里开始请求数据 格式:bytes=***-***(从指定开始到指定结束)  或者:bytes=***-(从指定位置到结束)
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",ChaosDownloadLength];

[request setValue:range forHTTPHeaderField:@"Range"];

_task = [self.session dataTaskWithRequest:request];

}
return _task;
}

// 开始
- (IBAction)startClick:(id)sender {

[self.task resume];
}
// 暂停
- (IBAction)pauseClick:(id)sender {

[self.task suspend];
}

- (void)viewDidLoad {
[super viewDidLoad];
}

#pragma mark - <NSURLSessionDataDelegate>
/**
* 接收到响应的时候调用
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
// 调用blcok,才能接受到数据
completionHandler(NSURLSessionResponseAllow);
// 初始化stream
self.stream = [NSOutputStream outputStreamToFileAtPath:ChaosFileFullPath append:YES];
[self.stream open];

// 获取文件总长度
self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + ChaosDownloadLength;

// 接收到服务器响应的时候存储文件的总长度到plist,实现多文件下载,先取出字典,给字典赋值最后写入。
// 错误做法:直接写入文件,会用这次写入的信息覆盖原来所有的信息
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:ChaosDownloadFilesPlist];
// 字典可能为空
if (dict == nil) dict = [NSMutableDictionary dictionary];
// 写入文件
dict[ChaosFileName] = @(self.totalLength);
[dict writeToFile:ChaosDownloadFilesPlist atomically:YES];
}

/**
* 接收到服务器发来的数据的时候调用 -- 有可能调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// 写入数据
[self.stream write:[data bytes] maxLength:data.length];
// 获取已经下载的长度
self.downloadLength = ChaosDownloadLength;
// 计算进度
NSLog(@"%f",1.0 * self.downloadLength / self.totalLength);
}

/**
* 任务完成的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"----finish");
[self.stream close];
self.stream = nil;

// 一个任务对应一个文件,用完清空
self.task = nil;
}
@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: