文件的下载分为NSURLConnection与NSURLSession两种,前一种有恨悠久的历史了。使用相对麻烦,后者是新出来的,增加了一些额外的功能。
一、NSURLConnection实现下载
TIPS:
1、当NSURLConnection下载时,得到的NSData写入文件时,data并没有占用多大内存. (即使文件很大)
2、一点点在传. 做的是磁盘缓存.而不是内存缓存机制。
3、了解在NSURLConnection上加代理。[consetDelegateQueue:[[NSOperationQueuealloc]init]]
4、NSURLResponse记录的了url, mineType, exceptedContentLength, suggestedFileName等属性. 下载时用得着.
以下程序实现追踪下载百分比的下载(URLConnection自带的方法):
- #import "XNDownload.h"
- typedef void(^ProgressBlock)(float percent);
- @interface XNDownload() <NSURLConnectionDataDelegate>
- @property (nonatomic, strong) NSMutableData *dataM;
- // 保存在沙盒中的文件路径
- @property (nonatomic, strong) NSString *cachePath;
- // 文件总长度
- @property (nonatomic, assign) long long fileLength;
- // 当前下载的文件长度
- @property (nonatomic, assign) long long currentLength;
- // 回调块代码
- @property (nonatomic, copy) ProgressBlock progress;
- @end
- @implementation XNDownload
- - (NSMutableData *)dataM
- {
- if (!_dataM) {
- _dataM = [NSMutableData data];
- }
- return _dataM;
- }
- - (void)downloadWithURL:(NSURL *)url progress:(void (^)(float))progress
- {
- // 0. 记录块代码
- self.progress = progress;
- // 1. request GET
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- // 2. connection
- NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
- // 让connection支持多线程,指定代理的工作队列即可
- // NSURLConnection在运行时,运行循环不负责监听代理的具体执行
- [connection setDelegateQueue:[[NSOperationQueue alloc] init]];
- // 3. 启动连接
- [connection start];
- }
- #pragma mark - 代理方法
- // 1. 接收到服务器的响应,服务器执行完请求,向客户端回传数据
- - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- {
- NSLog(@"%@ %lld", response.suggestedFilename, response.expectedContentLength);
- // 1. 保存的缓存路径
- NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
- self.cachePath = [cachePath stringByAppendingPathComponent:response.suggestedFilename];
- // 2. 文件总长度
- self.fileLength = response.expectedContentLength;
- // 3. 当前下载的文件长度
- self.currentLength = 0;
- // 清空数据
- [self.dataM setData:nil];
- }
- // 2. 接收数据,从服务器接收到数据
- - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- {
- // 拼接数据
- [self.dataM appendData:data];
- // 根据data的长度增加当前下载的文件长度
- self.currentLength += data.length;
- float progress = (float)self.currentLength / self.fileLength;
- // 判断是否定义了块代码
- if (self.progress) {
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- // 强制运行循环执行一次更新
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
- self.progress(progress);
- }];
- }
- }
- // 3. 完成接收
- - (void)connectionDidFinishLoading:(NSURLConnection *)connection
- {
- NSLog(@"%s %@", __func__, [NSThread currentThread]);
- // 将dataM写入沙盒的缓存目录
- // 写入数据,NSURLConnection底层实现是用磁盘做的缓存
- [self.dataM writeToFile:self.cachePath atomically:YES];
- }
- // 4. 出现错误
- - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- {
- NSLog(@"%@", error.localizedDescription);
- }
- @end
二、NSURLSession实现下载
NSURLSession能实现断点续传,暂停下载等功能。
1、session提供的是开了多个线程的异步下载.
2、下载的暂停与续传: (session的代理中的方法)
*弄一个NSData变量来保存下载东西.暂停时将下载任务task清空.
*续传:将暂停时的data交给session继续下载,并将先前的data清空.
3、task一定要resume才开始执行.
- #import "XNViewController.h"
- @interface XNViewController () <NSURLSessionDownloadDelegate>
- // 下载网络回话
- @property (nonatomic, strong) NSURLSession *session;
- // 下载任务
- @property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
- // 续传的二进制数据
- @property (nonatomic, strong) NSData *resumeData;
- @end
- @implementation XNViewController
- - (NSURLSession *)session
- {
- if (!_session) {
- NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
- _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
- }
- return _session;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- [self downloadFile];
- }
- // 暂停下载任务
- - (IBAction)pause
- {
- // 如果下载任务不存在,直接返回
- if (self.downloadTask == nil) return;
- // 暂停任务(块代码中的resumeData就是当前正在下载的二进制数据)
- // 停止下载任务时,需要保存数据
- [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
- self.resumeData = resumeData;
- // 清空并且释放当前的下载任务
- self.downloadTask = nil;
- }];
- }
- - (IBAction)resume
- {
- // 要续传的数据是否存在?
- if (self.resumeData == nil) return;
- // 建立续传的下载任务
- self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
- [self.downloadTask resume];
- // 将此前记录的续传数据清空
- self.resumeData = nil;
- }
- // 如果在开发中使用到缓存目录,一定要提供一个功能,“清除缓存”!
- /** 下载文件 */
- - (void)downloadFile
- {
- NSString *urlStr = @"http://localhost/苍老师全集.rmvb";
- urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSURL *url = [NSURL URLWithString:urlStr];
- // (1) 代理 & 直接启动任
- // 2. 启动下载任务
- self.downloadTask = [self.session downloadTaskWithURL:url];
- [self.downloadTask resume];
- }
- #pragma mark - 下载代理方法
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
- {
- NSLog(@"完成 %@ %@", location, [NSThread currentThread]);
- }
- /**
- bytesWritten : 本次下载的字节数
- totalBytesWritten : 已经下载的字节数
- totalBytesExpectedToWrite : 下载总大小
- */
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
- {
- float progress = (float)totalBytesWritten / totalBytesExpectedToWrite;
- [[NSOperationQueue mainQueue] addOperationWithBlock:^{
- //主线程中更新进度UI操作。。。。
- }];
- }
- /** 续传的代理方法 */
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
- {
- NSLog(@"offset : %lld", fileOffset);
- }
- @end