文件的下载分为NSURLConnection与NSURLSession两种,前一种有恨悠久的历史了。使用相对麻烦,后者是新出来的,增加了一些额外的功能。
一、NSURLConnection实现下载
TIPS:
1、
当
NSURLConnection
下载时
,
得到的
NSData
写入文件
时
,
data
并没有
占用多大内存
.
(
即使文件很大
)
2、
一点点在传
.
做的是磁盘缓存
.
而不是内存缓存机制。
3、了解在
NSURLConnection
上加代理。
[con
setDelegateQueue
:[[NSOperationQueue
alloc]
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
出处: http://blog.youkuaiyun.com/xn4545945