【转】iOS 文件下载及断点续传
ios的下载我们可以使用的方法有:NSData、NSURLConnection、NSURLSession还有第三方框架AFNetworking和ASI
利用NSData方法和NSURLConnection的异步方法仅适合下载较小的文件,因为NSData是把数据一口气下载,下载大文件那么手机的内存会一下子暴涨,大文件下载可以用NSURLConnection代理方法、NSURLSession代理方法、AFNetworking和ASI:
NSData:
// 在沙盒中得存储路径 NSString *str = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [str stringByAppendingPathComponent:@"test.exe"]; NSLog(@"%@",str); // 下载地址 NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"]; NSData *data = [NSData dataWithContentsOfURL:url]; // 数据写入到沙盒 [data writeToFile:path atomically:YES];
NSURLConnection异步方法:
// 在沙盒中得存储路径 NSString *str = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [str stringByAppendingPathComponent:@"test.exe"]; NSLog(@"%@",str); // 下载地址 NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 异步方法 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { // 数据写入沙盒 [data writeToFile:path atomically:YES]; }];
利用NSURLConnection的代理方法来实现下载(NSURLConnectionDataDelegate):
这其中使用了响应头expectedContentLength来获取文件的总大小,利用了请求头的键“Range”来设置暂停后下一次开始时的位置,利用了NSFileManager和NSFileHandle来设置分段存储下载的数据:
#pragma mark - 属性 // 文件句柄对象 @property (nonatomic,strong) NSFileHandle *fileHandle; // 文件总长度 @property (nonatomic,assign) long long contentLength; // 当前文件长度 @property (nonatomic,assign) long long currentLen; @property (nonatomic,strong) NSURLConnection *conn; #pragma mark - 点击事件 // 按钮点击事件 - (IBAction)startAndStop { // 设置按钮选中与否 _btn.selected = !_btn.selected; // 实现断点续传 if (_btn.selected){ // 按钮选中 // 下载地址 NSString *str = @"http://51.duote.org/haozip_duote.exe"; NSURL *url = [NSURL URLWithString:str]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 设置请求头数据 NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLen]; [request setValue:range forHTTPHeaderField:@"Range"]; self.conn = [NSURLConnection connectionWithRequest:request delegate:self]; }else{ // 取消下载(暂停) [self.conn cancel]; self.conn = nil; } } #pragma mark - NSURLConnectionDataDelegate代理 // 出错 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"出错 - %@",error.localizedDescription); } // 接收到响应 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { if (self.currentLen) return; NSString *cacheStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [cacheStr stringByAppendingPathComponent:@"test.exe"]; // 创建一个空的文件 NSFileManager *mgr = [NSFileManager defaultManager]; [mgr createFileAtPath:path contents:nil attributes:nil];
// 句柄对象实例化 self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:path]; // 文件总大小(响应头取得) NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response; self.contentLength = resp.expectedContentLength; } // 接收数据 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 找到文件的末尾 [self.fileHandle seekToEndOfFile]; // 在末尾写入数据 [self.fileHandle writeData:data]; // 当前已下载长度 self.currentLen += data.length; // 下载百分比:当前已下载大小 / 文件总大小 self.progressView.progress = (double)self.currentLen / self.contentLength; NSLog(@"%f",self.progressView.progress); } // 接收完成 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"接收完成"); // 关闭 [self.fileHandle closeFile]; self.fileHandle = nil; self.currentLen = 0; self.contentLength = 0; }
利用NSURLSession的代理方法:
NSURLSession是ios7后才有的方法,苹果官方旨在替代NSURLConnection
NSURLSessionDataTask:解决一般的GET\POST的请求方法
NSURLSessionDownloadTask:解决文件下载
NSURLSessionUploadTask:文件上传
A、NSURLSessionDataTask的操作:
// 下载地址 NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // 这里进行json或XML解析 }]; // 3.开始任务 [task resume];
B、NSURLSessionDownloadTask:不能看到下载进度
// session对象 NSURLSession *session = [NSURLSession sharedSession]; // 下载地址 NSString *str = @"http://51.duote.org/haozip_duote.exe"; // 创建一个下载task NSURL *url = [NSURL URLWithString:str]; NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { ////// 因为任务下载是存在沙盒的temp文件中,并且下载完成后会被立即清除,所以这里将下载的文件剪切到caches中 // location : 临时文件的路径(下载好的文件) NSString *cacheStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *path = [cacheStr stringByAppendingPathComponent:response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:path error:nil]; }]; // 开始任务 [task resume];
C:NSURLSessionDownloadDelegate的实现:
NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; // session对象 NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 下载task NSURL *url = [NSURL URLWithString:@"http://51.duote.org/haozip_duote.exe"]; NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url]; // 这里不要用block,如果用了block,那么方法会优先调用block,而不是代理方法
// 开始任务 [task resume]; #pragma mark - NSURLSessionDownloadDelegate代理 // 下载完毕后调用 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:file error:nil]; } // 恢复下载时调用 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } // 每当下载一部分就会调用,可能会被调用多次 // bytesWritten 这次下载了多少 // totalBytesWritten 当前总共下载了多少到沙盒 // totalBytesExpectedToWrite 文件的总大小 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { double progress = (double)totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"下载进度---%f", progress); }
D、NSURLSession实现断点续传
// .m文件 @property (nonatomic, strong) NSURLSessionDownloadTask *task; @property (nonatomic, strong) NSData *resumeData; @property (nonatomic, strong) NSURLSession *session; - (NSURLSession *)session { if (!_session) { // 获得session NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; } return _session; } // 按钮点击事件 - (IBAction)download:(UIButton *)sender { // 按钮状态取反 sender.selected = !sender.isSelected; if (self.task == nil) { // 开始(继续)下载 if (self.resumeData) { // 恢复 [self resume]; } else { // 开始 [self start]; } } else { // 暂停 [self pause]; } } // 开始下载 - (void)start { // 创建一个下载任务 NSURL *url = [NSURL URLWithString:@"http://xxx.com/mmg/cc.exe"]; self.task = [self.session downloadTaskWithURL:url]; // 开始任务 [self.task resume]; } // 恢复下载 - (void)resume { // 传入上次暂停下载返回的数据,就可以恢复下载 self.task = [self.session downloadTaskWithResumeData:self.resumeData]; // 开始任务 [self.task resume]; // 清空 self.resumeData = nil; } // 暂停下载 - (void)pause { __weak typeof(self) vc = self; [self.task cancelByProducingResumeData:^(NSData *resumeData) { // resumeData : 包含了继续下载的开始位置\下载的url vc.resumeData = resumeData; vc.task = nil; }]; } #pragma mark - NSURLSessionDownloadDelegate 下载代理方法 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致 NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; // 将临时文件剪切或者复制Caches文件夹 NSFileManager *mgr = [NSFileManager defaultManager]; // AtPath : 剪切前的文件路径 // ToPath : 剪切后的文件路径 [mgr moveItemAtPath:location.path toPath:file error:nil]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSLog(@"获得下载进度--%@", [NSThread currentThread]); // 获得下载进度 self.progressView.progress = (double)totalBytesWritten / totalBytesExpectedToWrite; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { }
from :http://www.cnblogs.com/GeekStar/p/4409714.html