iOS项目开发—文件下载功能的实现
一、简单说明
1.思路
把下载的data追加到文件的尾部,直到所有的数据下载完为止。
1.在连通了服务器的时候,创建一个空的文件到沙盒中NSFileManager(文件管理类)
2.创建写数据的文件句柄
3.在接收到服务器返回的数据后,把data写入到创建的空文件中,但是不能使用writeTofile(会覆盖)
3.1移动到文件的尾部
3.2从当前移动的位置,写入数据
4.服务器的数据加载完毕后关闭连接,不再输入数据在文件中
二、代码示例
1.代码:
新建一个类,让其继承自NSObject类,一个文件下载器只下载一个文件。
自定义类的TXFileDownloader.h代码:
1 // 2 // TXFileDownloader.h 3 // 文件下载 4 // 5 // Created by 鑫 on 14/12/23. 6 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 11 @interface TXFileDownloader : NSObject 12 //下载的远程url(连接到服务器的路径) 13 @property(nonatomic,strong)NSString *url; 14 //下载后的存储路径(文件下载到什么地方) 15 @property(nonatomic,strong)NSString *destPath; 16 //是否正在下载(只有下载器内部清楚) 17 @property(nonatomic,readonly,getter = isDownloading)BOOL Downloading; 18 //用来监听下载进度 19 @property(nonatomic,copy)void (^progressHandler)(double progress); 20 //用来监听下载完成 21 @property(nonatomic,copy)void (^completionHandler)(); 22 //用来监听下载错误 23 @property(nonatomic,copy)void(^failureHandler)(NSError *error); 24 -(void)pause; 25 -(void)start; 26 @end
自定义类的TXFileDownloader.m
1 // TXFileDownloader.m 2 // 文件下载 3 // 4 // Created by 鑫 on 14/12/23. 5 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 6 // 7 8 #import "TXFileDownloader.h" 9 @interface TXFileDownloader ()<NSURLConnectionDataDelegate> 10 //请求对象 11 @property(nonatomic,strong)NSURLConnection *cnnt; 12 //文件句柄 13 @property(nonatomic,strong)NSFileHandle *writeHandle; 14 //当前获取到的数据长度 15 @property(nonatomic,assign)long long currentLength; 16 //完整数据长度 17 @property(nonatomic,assign)long long sumLength; 18 @end 19 20 @implementation TXFileDownloader 21 //开始下载 22 -(void)start 23 { 24 _Downloading=YES; 25 26 //创建一个请求 27 NSURL *URL=[NSURL URLWithString:self.url]; 28 NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:URL]; 29 30 //设置请求头信息 31 //self.currentLength字节部分重新开始读取 32 NSString *value=[NSString stringWithFormat:@"bytes=%lld-",self.currentLength]; 33 [request setValue:value forHTTPHeaderField:@"Range"]; 34 35 //发送请求(使用代理的方式) 36 self.cnnt=[NSURLConnection connectionWithRequest:request delegate:self]; 37 38 } 39 40 //暂停下载 41 -(void)pause 42 { 43 _Downloading=NO; 44 //取消发送请求 45 [self.cnnt cancel]; 46 self.cnnt=nil; 47 } 48 49 #pragma mark- NSURLConnectionDataDelegate代理方法 50 /* 51 *当接收到服务器的响应(连通了服务器)时会调用 52 */ 53 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 54 { 55 #warning 判断是否是第一次连接 56 if (self.sumLength) return; 57 58 //1.创建文件存数路径 59 NSString *caches=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 60 NSString *filePath=[caches stringByAppendingPathComponent:@"video.zip"]; 61 62 63 64 //2.创建一个空的文件,到沙盒中 65 NSFileManager *mgr=[NSFileManager defaultManager]; 66 //刚创建完毕的大小是o字节 67 [mgr createFileAtPath:filePath contents:nil attributes:nil]; 68 69 //3.创建写数据的文件句柄 70 self.writeHandle=[NSFileHandle fileHandleForWritingAtPath:filePath]; 71 72 //4.获取完整的文件长度 73 self.sumLength=response.expectedContentLength; 74 } 75 76 /* 77 *当接收到服务器的数据时会调用(可能会被调用多次,每次只传递部分数据) 78 */ 79 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 80 { 81 //累加接收到的数据长度 82 self.currentLength+=data.length; 83 //计算进度值 84 double progress=(double)self.currentLength/self.sumLength; 85 // self.progress.progress=progress; 86 if (self.progressHandler) {//传递进度值给block 87 self.progressHandler(progress); 88 89 //相当于在此处调用了下面的代码 90 // ^(double progress) 91 // { 92 // //把进度的值,传递到控制器中进度条,以进行显示 93 // vc.progress.progress=progress; 94 // }; 95 } 96 97 98 //一点一点接收数据。 99 NSLog(@"接收到服务器的数据!---%d",data.length); 100 //把data写入到创建的空文件中,但是不能使用writeTofile(会覆盖) 101 //移动到文件的尾部 102 [self.writeHandle seekToEndOfFile]; 103 //从当前移动的位置,写入数据 104 [self.writeHandle writeData:data]; 105 } 106 107 /* 108 *当服务器的数据加载完毕时就会调用 109 */ 110 -(void)connectionDidFinishLoading:(NSURLConnection *)connection 111 { 112 NSLog(@"下载完毕----%lld",self.sumLength); 113 //关闭连接,不再输入数据在文件中 114 [self.writeHandle closeFile]; 115 self.writeHandle=nil; 116 117 //清空进度值 118 self.currentLength=0; 119 self.sumLength=0; 120 121 if (self.completionHandler) {//下载完成通知控制器 122 self.completionHandler(); 123 //相当于下面的代码 124 // ^{ 125 // NSLog(@"下载完成"); 126 // [self.btn setTitle:@"下载已经完成" forState:UIControlStateNormal]; 127 // } 128 } 129 130 } 131 /* 132 *请求错误(失败)的时候调用(请求超时\断网\没有网\,一般指客户端错误) 133 */ 134 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 135 { 136 if (self.failureHandler) {//通知控制器,下载出错 137 self.failureHandler(error); 138 //相当于调用了下面的代码 139 // ^{ 140 // NSLog(@"下载错误!"); 141 // } 142 } 143 } 144 145 @end
实现代码:
主控制器TXViewController.m
1 // 2 // TXViewController.m 3 // 文件下载 4 // 5 // Created by 鑫 on 14/12/23. 6 // Copyright (c) 2014年 梁镋鑫. All rights reserved. 7 // 8 9 #import "TXViewController.h" 10 #import "TXFileDownloader.h" 11 @interface TXViewController () 12 13 14 @property(nonatomic,strong)TXFileDownloader *fileDownloader; 15 @property (weak, nonatomic) IBOutlet UIButton *btn; 16 @property (weak, nonatomic) IBOutlet UIProgressView *progress; 17 @end 18 19 @implementation TXViewController 20 21 - (void)viewDidLoad 22 { 23 [super viewDidLoad]; 24 } 25 26 #pragma mark-懒加载 27 -(TXFileDownloader *)fileDownloader 28 { 29 if (_fileDownloader==nil) { 30 _fileDownloader=[[TXFileDownloader alloc]init]; 31 //设置文件下载路径 32 _fileDownloader.url=@"http://192.168.1.53:8080/MJServer/resources/video.zip"; 33 34 //设置文件保存路径 35 NSString *caches=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 36 NSString *filePath=[caches stringByAppendingPathComponent:@"video.zip"]; 37 _fileDownloader.destPath=filePath; 38 39 //获取10的真实类型,把它作为a的类型 40 // typeof(10) a = 20; // int a = 20; 41 __weak typeof(self) vc=self; 42 _fileDownloader.progressHandler=^(double progress) 43 { 44 vc.progress.progress=progress; 45 NSLog(@"%f",progress); 46 }; 47 _fileDownloader.completionHandler=^{ 48 NSLog(@"下载完成"); 49 [vc.btn setTitle:@"下载已经完成" forState:UIControlStateNormal]; 50 }; 51 _fileDownloader.failureHandler=^(NSError *error){ 52 NSLog(@"下载错误!%@",error); 53 }; 54 } 55 return _fileDownloader; 56 } 57 58 //点击下载按钮,处理操作 59 - (IBAction)star { 60 if (self.fileDownloader.isDownloading) {//如果正在下载,那么调用方法暂停 61 [self.fileDownloader pause]; 62 [self.btn setTitle:@"暂停" forState:UIControlStateNormal]; 63 }else//如果没有正在下载,那么调用下载方法 64 { 65 [self.fileDownloader start]; 66 [self.btn setTitle:@"下载" forState:UIControlStateNormal]; 67 } 68 } 69 70 @end
代码:
控制器想监听下载器的下载进度,控制器想监听下载器的下载完毕,控制器想监听下载器的下载失败等都可以使用block的方式进行。
打印查看:
2.代码说明
(1)创建文件存储路径(写入到沙盒)
NSString *caches=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath=[caches stringByAppendingPathComponent:@"video.zip"];
(2)创建一个空的文件夹( NSFileManager类的使用)
NSFileManager *mgr=[NSFileManager defaultManager];
(3)创建写数据的文件句柄
self.writeHandle=[NSFileHandle fileHandleForWritingAtPath:filePath];
(4)把data写入到创建的空文件中,但是不能使用writeTofile(会覆盖)
移动句柄到文件的尾部 [self.writeHandle seekToEndOfFile];
(5)下载完毕的时候,关闭连接
[self.writeHandle closeFile];
3、Range简单说明
通过设置请求头Range可以指定每次从网路下载数据包的大小
Range示例
bytes=0-499 从0到499的头500个字节
bytes=500-999 从500到999的第二个500字节
bytes=500- 从500字节以后的所有字节
bytes=-500 最后500个字节
bytes=500-599,800-899 同时指定几个范围
Range小结
- 用于分隔
前面的数字表示起始字节数
后面的数组表示截止字节数,没有表示到末尾
, 用于分组,可以一次指定多个Range,不过很少用
注意:关键代码
从self.currentLength字节部分重新开始读取
NSString *value=[NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
[request setValue:value forHTTPHeaderField:@"Range"];