摘自:http://www.cnblogs.com/kenshincui/p/4042190.html
使用代理方法
做过Web开发的朋友应该很清楚,Http是无连接的请求。每个请求request服务器都有一个对应的响应response,无论是asp.net、jsp、php都是基于这种机制开发的。
在Web开发中主要的请求方法有如下几种:
- GET请求:get是获取数据的意思,(请求)数据以明文在URL中传递,受限于URL长度,所以传输数据量比较小。
- POST请求:post是向服务器提交数据的意思,提交的数据以实际内容形式存放到消息头中进行传递,无法在浏览器url中查看到,大小没有限制。
- HEAD请求:请求头信息,并不返回请求数据体,而只返回请求头信息,常用用于在文件下载中取得文件大小、类型等信息。
在开发中往往数据存储在服务器端,而客户端(iOS应用)往往通过向服务器端发送请求从服务器端获得数据。要模拟这个过程首先当然是建立服务器端应用,应用的形式没有限制,你可以采用任何Web技术进行开发。假设现在有一个文件服务器,用户输入文件名称就可以下载文件。服务器端程序很简单,只要访问http://192.168.1.208/FileDownload.aspx?file=filename,就可以下载指定filename的文件,由于服务器端开发的内容不是今天的重点在此不再赘述。客户端界面设计如下图:
程序的实现需要借助几个对象:
NSURLRequest:建立了一个请求,可以指定缓存策略、超时时间。和NSURLRequest对应的还有一个NSMutableURLRequest,如果请求定义为NSMutableURLRequest则可以指定请求方法(GET或POST)等信息。
NSURLConnection:用于发送请求,可以指定请求和代理。当前调用NSURLConnection的start方法后开始发送异步请求。
程序代码如下:
// // KCMainViewController.m // UrlConnection // // Created by Kenshin Cui on 14-3-22. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "KCMainViewController.h" //注意要满足下面这两个协议 @interface KCMainViewController ()<NSURLConnectionDataDelegate,NSURLConnectionDelegate>{ NSMutableData *_data;//响应数据 UITextField *_textField; UIButton *_button; UIProgressView *_progressView; UILabel *_label; long long _totalLength; } @end @implementation KCMainViewController #pragma mark - UI方法 - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } #pragma mark - 私有方法 #pragma mark 界面布局 -(void)layoutUI{ //地址栏 _textField=[[UITextField alloc]initWithFrame:CGRectMake(10, 50, 300, 25)]; _textField.borderStyle=UITextBorderStyleRoundedRect; _textField.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0]; _textField.text=@"简约至上:交互式设计四策略.epub"; [self.view addSubview:_textField]; //进度条 _progressView=[[UIProgressView alloc]initWithFrame:CGRectMake(10, 100, 300, 25)]; [self.view addSubview:_progressView]; //状态显示 _label=[[UILabel alloc]initWithFrame:CGRectMake(10, 130, 300, 25)]; _label.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0]; [self.view addSubview:_label]; //下载按钮 _button=[[UIButton alloc]initWithFrame:CGRectMake(10, 500, 300, 25)]; [_button setTitle:@"下载" forState:UIControlStateNormal]; [_button setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal]; [_button addTarget:self action:@selector(sendRequest) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_button]; } #pragma mark 更新进度 -(void)updateProgress{ // [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (_data.length==_totalLength) { _label.text=@"下载完成";//这个可以放到connectionDidFinishLoading:(NSURLConnection *)connection接收完成方法中 }else{ _label.text=@"正在下载...";//这个可以放到connection:(NSURLConnection *)connection didReceiveData:(NSData *)data方法中 [_progressView setProgress:(float)_data.length/_totalLength]; } // }]; } #pragma mark 发送数据请求 -(void)sendRequest{ NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.208/FileDownload.aspx?file=%@",_textField.text]; //注意对于url中的中文是无法解析的,需要进行url编码(指定编码类型为utf-8) //另外注意url解码使用stringByRemovingPercentEncoding方法 urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //创建url链接 NSURL *url=[NSURL URLWithString:urlStr]; /*创建请求 cachePolicy:缓存策略 a.NSURLRequestUseProtocolCachePolicy 协议缓存,根据response中的Cache-Control字段判断缓存是否有效,如果缓存有效则使用缓存数据否则重新从服务器请求 b.NSURLRequestReloadIgnoringLocalCacheData 不使用缓存,直接请求新数据 c.NSURLRequestReloadIgnoringCacheData 等同于 SURLRequestReloadIgnoringLocalCacheData d.NSURLRequestReturnCacheDataElseLoad 直接使用缓存数据不管是否有效,没有缓存则重新请求 eNSURLRequestReturnCacheDataDontLoad 直接使用缓存数据不管是否有效,没有缓存数据则失败 timeoutInterval:超时时间设置(默认60s) */ NSURLRequest *request=[[NSURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0f]; //创建连接 NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:request delegate:self]; //启动连接 [connection start]; } #pragma mark - 连接代理方法 #pragma mark 开始响应 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ NSLog(@"receive response."); _data=[[NSMutableData alloc]init]; _progressView.progress=0; //通过响应头中的Content-Length取得整个响应的总长度 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields]; _totalLength = [[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue]; } #pragma mark 接收响应数据(根据响应内容的大小此方法会被重复调用) -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ NSLog(@"receive data."); //连续接收数据 [_data appendData:data];//appendData //更新进度 [self updateProgress]; } #pragma mark 数据接收完成 -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSLog(@"loading finish."); //数据接收完保存文件(注意苹果官方要求:下载数据只能保存在缓存目录) NSString *savePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; savePath=[savePath stringByAppendingPathComponent:_textField.text]; [_data writeToFile:savePath atomically:YES]; NSLog(@"path:%@",savePath); } #pragma mark 请求失败 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ //如果连接超时或者连接地址错误可能就会报错 NSLog(@"connection error,error detail is:%@",error.localizedDescription); } @end
运行效果:
需要注意:
- 根据响应数据大小不同可能会多次执行- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data方法。
- URL中不能出现中文(例如上面使用GET传参数时,file参数就可能是中文),需要对URL进行编码,否则会出错。
简化请求方法
当然,对于上面文件下载这种大数据响应的情况使用代理方法处理响应具有一定的优势(可以获得传输进度)。但是如果现响应数据不是文件而是一段字符串(注意web请求的数据可以是字符串或者二进制,上面文件下载示例中响应数据是二进制),那么采用代理方法处理服务器响应就未免有些太麻烦了。其实苹果官方已经提供了下面两种方法处理一般的请求:
+ (void)sendAsynchronousRequest:request: queue:queue:completionHandler:发送一个异步请求
+ (NSData *)sendSynchronousRequest: returningResponse: error:发送一个同步请求