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
View Code

实现代码:

主控制器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
View Code

代码:

 

控制器想监听下载器的下载进度,控制器想监听下载器的下载完毕,控制器想监听下载器的下载失败等都可以使用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"];

原文地址:https://www.cnblogs.com/asd5551680/p/4180431.html