少量的数据进行网络传输,根本就满足不了需求。下载文档、音乐、视频等等都需要应用的支持,恰好NSURLSessionDownloadTask能够满足这些需求。
- NSURLSessionDownloadTask介绍
- NSURLSessionDownloadDelegate介绍
- 使用NSURLSessionDownloadTask进行下载
- 使用NSURLSessionDownloadTask进行断点下载
- 总结
NSURLSessionDownloadTask介绍
NSURLSessionDownloadTask支持三种任务类型中的下载任务类型,它提供断点下载,因网络原因导致下载没有完成时,它会保留已经下载完的数据,然后返回后ResumeData。API使用者可以利用resumeData进行后续下载。
这三个方法是声明在NSURLSession类里:
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; 通过URL对象来创建一个下载任务
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;通过NSURLRequest对象来创建一个下载任务
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;通过恢复数据来创建一个下载任务;
这个方法是声明在NSURLSessionDownloadTask类里:
- (void)cancelByProducingResumeData:(void (^)(NSData * _Nullable resumeData))completionHandler; //调用此方法停止下载任务,该方法通过block返回下载未完成的数据信息,可以通过该数据进行断点下载;
//可以发现,下面的信息还是很清晰的,例如下载地址、接收了多少数据、临时文件名等。都是后续断点下载必要的一些信息。
resumeData = /*
<plist version="1.0">
<dict>
<key>NSURLSessionDownloadURLkey>
<string>https://opensource.apple.com/tarballs/xnu/xnu-3789.70.16.tar.gzstring>
<key>NSURLSessionResumeBytesReceivedkey>
<integer>86594integer>
<key>NSURLSessionResumeCurrentRequestkey>
<data>
YnBsaXN0MDDUAQIDBAUGdXZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
AAGGoK8QFwcILEdNTlRVVlcrWDlZWmZnaGlqa2xxVSRudWxs3xAfCQoLDA0ODxAREhMU
FRYXGBkaGxwdHh8gISIjJCUmJygpKSssLS4vMDApLzQrKTY3ODk6OykpPjspL0JDLUVS
JDFfECBfX25zdXJscmVxdWVzdF9wcm90b19wcm9wX29ial8yMF8QIF9fbnN1cmxyZXF1
ZXN0X3Byb3RvX3Byb3Bfb2JqXzIxXxAQc3RhcnRUaW1lb3V0VGltZV8QHnJlcXVpcmVz
U2hvcnRDb25uZWN0aW9uVGltZW91dF8QIF9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bf
b2JqXzEwViRjbGFzc18QIF9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzExXxAg
X19uc3VybHJlcXVlc3RfcHJvdG9fcHJvcF9vYmpfMTJfECBfX25zdXJscmVxdWVzdF9w
cm90b19wcm9wX29ial8xM18QGl9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3BzXxAgX19u
c3VybHJlcXVlc3RfcHJvdG9fcHJvcF9vYmpfMTRfECBfX25zdXJscmVxdWVzdF9wcm90
b19wcm9wX29ial8xNV8QGnBheWxvYWRUcmFuc21pc3Npb25UaW1lb3V0XxAgX19uc3Vy
bHJlcXVlc3RfcHJvdG9fcHJvcF9vYmpfMTZfEBRhbGxvd2VkUHJvdG9jb2xUeXBlc18Q
IF9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzE3XxAgX19uc3VybHJlcXVlc3Rf
cHJvdG9fcHJvcF9vYmpfMThSJDBfECBfX25zdXJscmVxdWVzdF9wcm90b19wcm9wX29i
al8xOV8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzlfEB9fX25zdXJscmVx
dWVzdF9wcm90b19wcm9wX29ial84XxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJvcF9v
YmpfN18QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzZfEB9fX25zdXJscmVx
dWVzdF9wcm90b19wcm9wX29ial81XxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJvcF9v
YmpfNF8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzNSJDJfEB9fX25zdXJs
cmVxdWVzdF9wcm90b19wcm9wX29ial8xXxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJv
cF9vYmpfMF8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzIQCYAAgAAjAAAA
AAAAAAAIgAKAFoAHgAqACoAAgAeAC4AAEACADIANEAKADoAIgACAAIAJgAiAAIAHEBaA
A4ACgAYI00gPSSlLTFdOUy5iYXNlW05TLnJlbGF0aXZlgACABYAEXxA/aHR0cHM6Ly9v
cGVuc291cmNlLmFwcGxlLmNvbS90YXJiYWxscy94bnUveG51LTM3ODkuNzAuMTYudGFy
Lmd60k9QUVJaJGNsYXNzbmFtZVgkY2xhc3Nlc1VOU1VSTKJRU1hOU09iamVjdCNATgAA
AAAAABAACRAEE///////////U0dFVNNbXA9dYWVXTlMua2V5c1pOUy5vYmplY3Rzo15f
YIAPgBCAEaNiY2SAEoATgBSAFV8QD0FjY2VwdC1FbmNvZGluZ1ZBY2NlcHRfEA9BY2Nl
cHQtTGFuZ3VhZ2VdZ3ppcCwgZGVmbGF0ZVMqLypVZW4tdXPST1Btbl8QE05TTXV0YWJs
ZURpY3Rpb25hcnmjb3BTXxATTlNNdXRhYmxlRGljdGlvbmFyeVxOU0RpY3Rpb25hcnnS
T1Byc1xOU1VSTFJlcXVlc3SidFNcTlNVUkxSZXF1ZXN0XxAPTlNLZXllZEFyY2hpdmVy
0Xd4XxAbTlNLZXllZEFyY2hpdmVSb290T2JqZWN0S2V5gAEACAARABoAIwAtADIANwBR
AFcAmACbAL4A4QD0ARUBOAE/AWIBhQGoAcUB6AILAigCSwJiAoUCqAKrAs4C8AMSAzQD
VgN4A5oDvAO/A+EEAwQlBCcEKQQrBDQENQQ3BDkEOwQ9BD8EQQRDBEUERwRJBEsETQRP
BFEEUwRVBFcEWQRbBF0EXwRhBGMEZQRnBGgEbwR3BIMEhQSHBIkEywTQBNsE5ATqBO0E
9gT/BQEFAgUEBQ0FEQUYBSAFKwUvBTEFMwU1BTkFOwU9BT8FQQVTBVoFbAV6BX4FhAWJ
BZ8FowW5BcYFywXYBdsF6AX6Bf0GGwAAAAAAAAIBAAAAAAAAAHkAAAAAAAAAAAAAAAAA
AAYd
data>
<key>NSURLSessionResumeEntityTagkey>
<string>"7edc8d770864146649f38a5d6034f5a5"string>
<key>NSURLSessionResumeInfoTempFileNamekey>
<string>CFNetworkDownload_9puQxd.tmpstring>
<key>NSURLSessionResumeInfoVersionkey>
<integer>2integer>
<key>NSURLSessionResumeOriginalRequestkey>
<data>
YnBsaXN0MDDUAQIDBAUGUFFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
AAGGoKwHCCQ7QUJISUojS0xVJG51bGzfEBkJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAh
IiMkJSYnKCgqJywjLS4vKionLyonNjclOVIkMV8QEHN0YXJ0VGltZW91dFRpbWVfEB5y
ZXF1aXJlc1Nob3J0Q29ubmVjdGlvblRpbWVvdXRfECBfX25zdXJscmVxdWVzdF9wcm90
b19wcm9wX29ial8xMFYkY2xhc3NfECBfX25zdXJscmVxdWVzdF9wcm90b19wcm9wX29i
al8xMV8QIF9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzEyXxAgX19uc3VybHJl
cXVlc3RfcHJvdG9fcHJvcF9vYmpfMTNfEBpfX25zdXJscmVxdWVzdF9wcm90b19wcm9w
c18QIF9fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzE0XxAgX19uc3VybHJlcXVl
c3RfcHJvdG9fcHJvcF9vYmpfMTVfEBpwYXlsb2FkVHJhbnNtaXNzaW9uVGltZW91dF8Q
FGFsbG93ZWRQcm90b2NvbFR5cGVzUiQwXxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJv
cF9vYmpfOV8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzhfEB9fX25zdXJs
cmVxdWVzdF9wcm90b19wcm9wX29ial83XxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJv
cF9vYmpfNl8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzVfEB9fX25zdXJs
cmVxdWVzdF9wcm90b19wcm9wX29ial80XxAfX19uc3VybHJlcXVlc3RfcHJvdG9fcHJv
cF9vYmpfM1IkMl8QH19fbnN1cmxyZXF1ZXN0X3Byb3RvX3Byb3Bfb2JqXzFfEB9fX25z
dXJscmVxdWVzdF9wcm90b19wcm9wX29ial8wXxAfX19uc3VybHJlcXVlc3RfcHJvdG9f
cHJvcF9vYmpfMhAJIwAAAAAAAAAACIACgAuAB4AJgAmAAIAHgAoQABACgAiAAIAAgAeA
CIAAgAcQEIADgAKABgjTPA09Kj9AV05TLmJhc2VbTlMucmVsYXRpdmWAAIAFgARfED9o
dHRwczovL29wZW5zb3VyY2UuYXBwbGUuY29tL3RhcmJhbGxzL3hudS94bnUtMzc4OS43
MC4xNi50YXIuZ3rSQ0RFRlokY2xhc3NuYW1lWCRjbGFzc2VzVU5TVVJMokVHWE5TT2Jq
ZWN0I0BOAAAAAAAAEAAJE///////////0kNETU5cTlNVUkxSZXF1ZXN0ok9HXE5TVVJM
UmVxdWVzdF8QD05TS2V5ZWRBcmNoaXZlctFSU18QG05TS2V5ZWRBcmNoaXZlUm9vdE9i
amVjdEtleYABAAgAEQAaACMALQAyADcARABKAH8AggCVALYA2QDgAQMBJgFJAWYBiQGs
AckB4AHjAgUCJwJJAmsCjQKvAtEC1AL2AxgDOgM8A0UDRgNIA0oDTANOA1ADUgNUA1YD
WANaA1wDXgNgA2IDZANmA2gDagNsA24DcANxA3gDgAOMA44DkAOSA9QD2QPkA+0D8wP2
A/8ECAQKBAsEFAQZBCYEKQQ2BEgESwRpAAAAAAAAAgEAAAAAAAAAVAAAAAAAAAAAAAAA
AAAABGs=
data>
<key>NSURLSessionResumeServerDownloadDatekey>
<string>Wed, 06 Sep 2017 22:07:41 GMTstring>
dict>
plist>
*/
NSURLSessionDownloadDelegate介绍
NSURLSessionDownloadDelegate 下载任务的代理对象,任务的进度、数据以及通知都会调用该代理的方法,使用者需实现该代理方法接收这些信息。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location; //下载完成后,调用此方法,它会提供一个临时文件地址,该地址里的文件是下载内容的存储。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; //提供关于下载进度状态信息。它可以让我们实现合理的提示信息;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes; //下载恢复时调用此方法;如果你想获取用于恢复下载的数据,可以监听“NSURLSessionDownloadTaskResumeData”这个key,来获取ResumeData。如果一个下载发生了错误,它会通过通知传递- userInfo dictionary;
使用NSURLSessionDownloadTask进行下载
//创建会话类型
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:[[downloadTaskDelegate alloc] init] delegateQueue:[NSOperationQueue mainQueue]];
//创建下载任务
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithURL:url];
//执行下载任务
[downloadTask resume];
//代理实现 头文件
@interface downloadTaskDelegate : NSObject
@end
//实现文件
@implementation downloadTaskDelegate
/**
* NSURLSession 提供一个临时文件地址,供下载内容存储。
* Note:注意,当此方法返回时,该临时文件会被删除,必须在临时文件删除之前,让该文件永久存储。
* #param :(NSURLSession *)session 会话类型对象
* #param :(NSURLSessionDownloadTask *)downloadTask 任务类型对像
* #param :(NSURL *)location 临时文件的地址
*/
/* Sent when a download task that has completed a download. The delegate should
* copy or move the file at the given location to a new location as it will be
* removed when the delegate message returns. URLSession:task:didCompleteWithError: will
* still be called.
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
//在此方法返回之前,打开临时文件并读取该内容。
NSError *error = nil;
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:location error:&error];
//通过NSFileHandle的readDataToEndOfFile方法,我们发现,该临时文件还存在。
NSLog(@"%@",[[NSString alloc] initWithData:[fileHandle readDataToEndOfFile] encoding:NSUTF8StringEncoding]);
[fileHandle closeFile];
/**
* 保存临时文件,因为此方法返回,临时文件就会被删除
* 1. 获取文件管理对象
* 2. 打开Cache目录
* 3. 移动临时文件到Cache目录
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *cacheDirectory = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject];
cacheDirectory = [cacheDirectory URLByAppendingPathComponent:@"localTmp"];
NSError *moveError = nil;
//这是为了防止,文件存在会copy错误。所以文件名写死不是应该好方法,应该写成动态的。
[fileManager removeItemAtURL:cacheDirectory error:&moveError];
//这里为了演示,临时文件在该方法返回后会被删除,所以是copy临时文件,而不是移动。
if ([fileManager copyItemAtURL:location toURL:cacheDirectory error:&moveError]) {
NSLog(@"location:%@",location);
NSLog(@"%@",cacheDirectory);
}
//如果失败,打印错误信息
NSLog(@"%@",moveError);
}
/**
* 提供关于下载进度的状态信息。这个可以帮助我们实现更好的进度条。
* #param :(NSURLSession *)session 会话类型对象
* #param :(NSURLSessionDownloadTask *)downloadTask 任务类型对像
* #param :(int64_t)bytesWritten 每秒下载多少数据 bytes
* #param :(int64_t)totalBytesWritten 总共写入多少数据 bytes
* #param :(int64_t)totalBytesExpectedToWrite 期望的数据,也就是下载文件的大小 bytes。
*/
/* Sent periodically to notify the delegate of download progress. */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(@"Session %@ download task %@ wrote an additional %lld bytes (total %lld bytes) out of an expected %lld bytes.\n", session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
/**
* 下载恢复时调用此方法;如果你想获取用于恢复下载的数据,可以监听“NSURLSessionDownloadTaskResumeData”这个key,来获取ResumeData。
* #param:(int64_t)fileOffset 上次下载的偏移量,比如上次下载了50个字节中断了,恢复下载的时候,就是从这个偏移量开始。
* #param :(int64_t)expectedTotalBytes 期望的数据,也就是下载文件的大小 bytes
*
*/
/* Sent when a download has been resumed. If a download failed with an
* error, the -userInfo dictionary of the error will contain an
* NSURLSessionDownloadTaskResumeData key, whose value is the resume
* data.
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"Session %@ download task %@ resumed at offset %lld bytes out of an expected %lld bytes.\n", session, downloadTask, fileOffset, expectedTotalBytes);
}
注意:下载文件会存储在一个临时文件中,如果不在didFinishDownloadingToURL,进行相应的处理,这个临时文件在该方法执行完毕后被删除
使用NSURLSessionDownloadTask进行断点下载
//创建会话类型
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:[[downloadTaskDelegate alloc] init] delegateQueue:[NSOperationQueue mainQueue]];
//通过resumeData信息来创建断点续传下载任务
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithResumeData:resumeData];
//执行下载任务
[downloadTask resume];
/**
* 下载恢复时调用此方法;如果你想获取用于恢复下载的数据,可以监听“NSURLSessionDownloadTaskResumeData”这个key,来获取ResumeData。
* #param:(int64_t)fileOffset 上次下载的偏移量,比如上次下载了50个字节中断了,恢复下载的时候,就是从这个偏移量开始。
* #param :(int64_t)expectedTotalBytes 期望的数据,也就是下载文件的大小 bytes
*
*/
/* Sent when a download has been resumed. If a download failed with an
* error, the -userInfo dictionary of the error will contain an
* NSURLSessionDownloadTaskResumeData key, whose value is the resume
* data.
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"Session %@ download task %@ resumed at offset %lld bytes out of an expected %lld bytes.\n", session, downloadTask, fileOffset, expectedTotalBytes);
}
总结
- 进行文件下载的时候,一定要注意保存临时文件,否则临时文件会被删除。
- 如果想支持断点续传,要保存ResumData数据,该数据可以通过监听或者实现代理方法进行获得。
- 这里用的是默认的会话类型,也可以用后台会话类型创建下载任务。
参考资料
官方文档:URL Session Programming Guide