网络编程之NSURLSession

网络编程之NSURLSession 

1.NSURLSession简介 

1.1 What`s NSURLSession .

􏰢􏱚􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪  NSURLSession是一系列类的总称,核心类包括:NSURLRequest、NSCache、 NSURLSession、NSURLSessionConfiguration、NSURLSessionTask。 

1.2What's the use of NSURLSession。

  NSURLSession帮助我们快速的实现网络请求,并且非常方便的管理网络请求任务,例如后台任务,暂停继续任务等等。

1.3 How to use it --NSURLSession 

NSURLSession:对话对象,可以获取全局的,也可以自己创建。 􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪 

1 + (NSURLSession *)sharedSession;
2 
3 􏰢􏱚􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
4 
5 + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id<NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;

NSURLSessionConfiguration:用于配置NSURLSession对象,比如缓存策略等等。有 三种常用配置􏰢􏱚􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪 

  • 认配置将请求数据持久化保存,程序关闭也不会清除。(y使用的是默认设置)

    1 + (NSURLSessionConfiguration *)defaultSessionConfiguration;

  • 短暂型配置会将数据保存在缓存文件夹中,当程序退出时清空。

    + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;

  • 后台配置将会使NSURLSession对话可以在后台中运行,并在恢复到前台时给程 序反馈                          

  + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NNString *)identifier; 

NSURLSessionTask:用于通过不同的数据获取方式创建不同的任务,通过task管理任 务。比如task可以暂停或者继续等等。每一个网络请求都可以看多一个任务, NSURLSessionTask一般不会直接使用,而是使用子类 

  • 􏰢􏱚􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪􏰫􏰅􏰵NSURLSessionDataTask: 一般的数据请求任务
  • NSURLSessionDownloadTask: 文件的下载请求任务
  • NSURLSessionUploadTask: 文件的上传任务 

还有一个情况是当应用程序处于后台下载时,用户杀死了程序,这时之前在 NSURLSessionConfiguration设置的NSString类型的ID起作用了,当ID相同的时候,一旦生成 Session对象并设置Delegate,马上可以收到上一次关闭程序之前没有汇报工作的Task的结束情况(成功或者失败)。但是当ID不相同,这些情况就收不到,因此为了不让自己的消息被别的应用程序收到,或者收到别的应用程序的消息起见ID还是和程序的Bundle名称绑定上比较好,至少保证唯一性 。

注:以上内容来自zippowxk原创文章,请联系luxuntec@163.com 

 2.NSURLSession 使用

2.1 数据任务——NSURLSessionDataTask 

接下来我们看一下如何使用NSURLSession.首先需要设置一个全局的_session,然后使用默认配置,初始化(在viewDidload中).

1     //默认配置
2     NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
3     //初始化
4     _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
 1 #pragma mark -
 2 #pragma mark -  配置默认session
 3 - (void)dataTask {
 4     
 5     //获取地址
 6     NSURL *url = [NSURL URLWithString:@"https://www.baidu.com/img/bd_logo1.png"];
 7     
 8     //设置超时
 9 //    double timeout = 10;
10 //    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:timeout];
11     
12     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
13     
14     //设置超时
15     [request setTimeoutInterval:10];
16 
17     NSURLSessionDataTask *data_task = [_session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
18         NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
19     }];
20     
21     [data_task resume];
22 }

 

2.2 下载任务——NSURLSessionDownloadTask 

接下来我们看一下下载的情况。

 1 #pragma mark-
 2 #pragma mark- 下载过程
 3 - (void)downloadTask {
 4     
 5     NSURL *url = [NSURL URLWithString:@"https://www.baidu.com/img/bd_logo1.png"];
 6     
 7     //数据请求
 8     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10];
 9     
10     NSURLSessionDownloadTask *download_task = [_session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
11         
12 #warning 图片显示只在block中有效,执行完毕后,tmp文件就会被删除(在block中 NSLog 之后打断点,然后查看文件)
13         NSLog(@"%@",location);
14         
15     }];
16     
17     [download_task resume];
18     
19 }
 
加入断点之后复制路径:/Users/Macx/Library/Developer/CoreSimulator/Devices/8E5EC0C5-2268-4460-8C01-FC9D5CF38B2B/data/Containers/Data/Application/B7DA21BB-7246-460C-9E18-35B966B98C6B/tmp/CFNetworkDownload_N6PQRi.tmp
 
这是找到的文件:baidu 图片

 

当继续运行的时候,文件就会消失,原因:图片显示只在block中有效,执行完毕后,tmp文件就会被删除。如图:

 

2.2.1 下载进度 

提到下载,就要说一下下载进度的问题,在下载过程中用户关心的就是下载进度,当然,下载快慢跟网速关系最大,电脑的配置等。而在iOS中呢,在下载时,UI经常需要监听下载进度,NSURLSessionDownloadTask给了很好的解决方案, 创建任务时我们不能使用block的方式: NSURL *url = [NSURL URLWithString:@"http://ww1.sinaimg.cn/bmiddle/005wayyCgw1ew6yyvcv7vj30fa0fz40g.jpg"]; 

 1  NSURLSessionDownloadTask *download_task = [_session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {    
 2 #warning 图片显示只在block中有效,执行完毕后,tmp文件就会被删除(在block中 NSLog 之后打断点,然后查看文件)
 4         NSLog(@"%@",location);
 5     }]; 
 6 将下载中block的代码换作以下代码,代码的书写如下:
 7  //下载进度
 8  NSURLSessionDownloadTask *download_task = [_session downloadTaskWithRequest:request];

 同时还需要加入代理方法:

1 @protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
2 @protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>
3 @protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
4 @protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
5 @protocol NSURLSessionDelegate <NSObject>

可以看出,前四种代理方法都继承于同一个代理方法,而我们使用的是NSURLSessionDownloadDelegate,因此,不用在写NSURLSessionTaskDelegate,即使用其子类的代理方法即可--代理可以继承。

代理方法(一)

 1 //每次下载一定数据后调用
 2 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
 3     //bytesWritten 本次获取的length
 4     //totalBytesWritten  已经获取的length
 5     //totalBytesExpectedToWrite  需要获取的总长度length
 6     
 7     //打印输出 已经获取的length、需要获取的总长度content_length
 8     NSLog(@"%lld %lld",totalBytesWritten,totalBytesExpectedToWrite);
 9 #warning  需要大一点的文件才可以看出变化
10     //打印输出进度百分比
11     NSLog(@"%.2f%%",100 * (float)totalBytesWritten/(float)totalBytesExpectedToWrite);
12 }

 代理方法(二)

1 //下载完成后调用
2 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
3     //打印输出文件地址
4     NSLog(@"%@",location);
5 }

 

2.3 上传任务——NSURLSessionUploadTask 

上传任务一般为POST请求方式,需要设置NSURLRequest的请求体,然后把请求体作为发送 的数据发送出去。 

 2.3.1 POST请求

POST请求体的Content-type有很多种,最常用的是form-data 即表单数据。拼接form表单的方式如下 :

 Content-type: multipart/form-data, boundary=AaB03x
􏰑􏰊􏰕􏰘􏰰􏰝􏱂􏰇
以下是请求体内容
--AaB03x
content-disposition: form-data; name="field1"
此处应该空行 Joe Blow --AaB03x content-disposition: form-data; name="pics"; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --AaB03x--
  • 首先在请求头中要明确
  • 请求体的格式和间隔符(间隔符只有form-data才使用)
  • 然后两个空白行然后是表单中的数据 

 接下来是拼接代码:

 1 #pragma mark-拼接请求体
 2 +(NSData *)httpFormDataBodyWithBoundary:(NSString *)boundary
 3                                  params:(NSDictionary *)params
 4                               fieldName:(NSString *)fieldName
 5                                fileName:(NSString *)fileName
 6                         fileContentType:(NSString *)fileContentType
 7                                    data:(NSData *)fileData {
 8     
 9     NSString *preBoundary = [NSString stringWithFormat:@"--%@",boundary];
10     NSString *endBoundary = [NSString stringWithFormat:@"--%@--",boundary];
11     NSMutableString *body = [[NSMutableString alloc] init];
12     //遍历
13     for (NSString *key in params) {
14         //得到当前的key
15         //如果key不是当前的pic,说明value是字符类型,比如name:Boris
16         //添加分界线,换行,必须使用\r\n
17         [body appendFormat:@"%@\r\n",preBoundary];
18         //添加字段名称换2行
19         [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
20         //添加字段值
21         [body appendFormat:@"%@\r\n",[params objectForKey:key]];
22         
23     }
24     //添加分界线,换行
25     [body appendFormat:@"%@\r\n",preBoundary];
26     //声明pic字段,文件名为boris.png
27     [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",fieldName,fileName];
28     //声明上传文件的格式
29     [body appendFormat:@"Content-Type: %@\r\n\r\n",fileContentType];
30     //声明结束符
31     NSString *endStr = [NSString stringWithFormat:@"\r\n%@",endBoundary];
32     //声明myRequestData,用来放入http  body
33     NSMutableData *myRequestData = [NSMutableData data];
34     //将body字符串转化为UTF8格式的二进制
35     [myRequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
36     //将image的data加入
37     [myRequestData appendData:fileData];
38     //加入结束符--hwg--
39     [myRequestData appendData:[endStr dataUsingEncoding:NSUTF8StringEncoding]];
40     return myRequestData;
41 }

该类方法是自己拼写出来。复制于zippowxk原创文章

拼接出请求体后,就可以发送数据了。面使用微博上传图片的接口测试:https://upload.api.weibo.com/2/statuses/upload.json

可以用微博接口上传测试,这里可以访问 http://open.weibo.com/wiki/2/statuses/public_timeline 来注册。具体方法不赘述。

 接下来看代码:

 1 #pragma mark- 上传文件 POST/GET请求
 2 - (void)uploadTask {
 3     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.weibo.com/2/statuses/public_timeline.json"]];
 4     //设置HTTP请求方式  GET / POST
 5     [request setHTTPMethod:@"POST"];
 6     //设置请求头
 7     NSString *boundary = @"hwg";
 8     [request setValue:[NSString stringWithFormat: @"multipart/form-data;%@", boundary]forHTTPHeaderField:@"Content-type"];
 9     
10     //设置请求体
11     //获取上传的图片的data
12     NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"xiaoxin" ofType:@"jpeg"]];
13     //此处添加需要看清楚内容
14     NSData *body =  [ViewController httpFormDataBodyWithBoundary:boundary params:@{@"access_token":@"2.00cYYKWF6EKpiB3883361b1dJiZ4eD",@"status":@"哈哈,这是我测试NSURLSession上传文件的微博"} fieldName:@"pic" fileName:@"pic.png" fileContentType:@"image/png" data:data];
15     
16     //可以设置request的请求体,也可以在后面 fromData中添加请求体
17     /*
18     [request setHTTPBody:body];
19     NSURLSessionUploadTask *upload_task = [_session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
20         NSLog(@"upload  success");
21     }];
22      */
23     NSURLSessionUploadTask *upload_task = [_session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
24         NSLog(@"upload  success");
25     }];
26     //必须要 resume
27     [upload_task resume];
28 }

2.3.2 上传进度 

上传进度跟下载进度差不多。也需要代理方法,如下:

1 //发送完成时候调用
2 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
3 
4 //发送数据时候持续调用
5 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;

2.4 下载之断点续传 

在我们下载数据时候,可能由于各种原因而中止下载,而后再次打开可以继续下载--断点续传。

  1. 首先要暂停下载
  2. 将暂停后数据(当前数据)保存起来--_resumeData = resumeData;
  3. 继续下载时候回调代理方法。

 代码如下:在界面中加一个button按钮,便于测试。其中设置全局变量

 1 {
 2 
 3 //设置下载全局变量
 4 
 5     //判断是否正在下载
 6 
 7     BOOL isDownloading;
 8 
 9     NSURLSessionDownloadTask *_downloadTask;
10 
11     NSData *_resumeData; 
12 
13 }

 

 1 //断点续传
 2 - (IBAction)buttonAct:(UIButton *)sender {
 3     
 4     if (isDownloading) {
 5         //按钮变化
 6         [sender setTitle:@"继续下载" forState:UIControlStateNormal];
 7         //改变下载状态
 8         isDownloading = NO;
 9         //如果是可恢复的下载任务,应该先将数据保存到_resumeData中,注意在这里不要调用cancel方法
10         //[_downloadTask cancel];这样不可以
11         [_downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
12             //保存暂停后当前数据
13             _resumeData = resumeData;
14             _downloadTask = nil;
15         }];
16         
17     }else {
18         [sender setTitle:@"暂停下载" forState:UIControlStateNormal];
19         isDownloading = YES;
20         NSURLSessionDownloadTask *download_task = [_session downloadTaskWithResumeData:_resumeData];
21         _downloadTask = download_task;
22         //需要再次运行
23         [download_task resume];
24     }
25 }

 


最后,提示一点:

3. iOS 9 更新内容
3.1 iOS9更新之——HTTPS
3.2 iOS9更新之——NSURLSessionStreamTask 

 文章错误之处请指教,如需转载请标明出处。

 

 

 

 

􏰢􏱚􏱣􏰴􏱧􏰜􏰠􏰇􏰫􏰚􏰁􏱗􏱩􏱗􏰖􏰤􏱦􏱆􏰀􏱖􏰜􏱅􏰙􏰂􏰤􏰎􏱝􏰃􏱟􏱀􏰜􏰬􏱪􏰫 

posted @ 2015-09-30 09:39  戎马一书生  阅读(370)  评论(0编辑  收藏  举报