IOS网络访问之NSURLConnection

IOS网络访问主要建立在http协议上

IOS提供了几个重要的对象完成http请求响应

NSURLRequest:代表一个请求,通过NSURLRequest可以设置请求的URL地址以及缓存策略

NSMutableURLRequest:NSURLRequest的子类,可以方便地设置请求头的各种信息以及请求方式

NSURLConnection:网络访问对象,可以通过同步或者异步的方式发送请求

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //访问的URL
    NSString *str = @"http://www.baidu.com/";
    //对URL进行编码
    str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    //生成NSURLRequest,timeoutInterval表示超时时间,默认60秒
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:str] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];

    NSURLResponse *resp = [[NSURLResponse alloc] init];
    NSError *error = [[NSError alloc] init];
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&resp error:&error];
    NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}

 

上面代码访问了百度首页,如果URL中包含中文字符必须先经过编码

其中NSURLRequest初始化方法中的cachePolicy表示缓存策略,它可以设置成以下的值

NSURLRequestUseProtocolCachePolicy  根据response中的Cache-Control字段判断缓存是否有效,如果缓存有效则使用缓存数据否则重新从服务器请求 

NSURLRequestReloadIgnoringLocalCacheData   不使用缓存,直接从服务器请求数据

NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData  以上一个值意义相同

NSURLRequestReturnCacheDataElseLoad  如果缓存有效则使用缓存否则从服务器请求数据

NSURLRequestReturnCacheDataDontLoad  如果缓存有效则使用缓存否则请求失败

 

异步请求

sendSynchronousRequest使用的是同步方法访问网络,会阻塞主线程,一般我们常用异步方式访问网络,可以通过多种方式实现异步请求

首先我们写个简短的服务器代码,接收请求参数然后输出

//iostest.php
<?php
    $value1 = $_POST["name"];
    $value2 = $_POST["gender"];
    echo $value1.‘|‘.$value2;
?>

方式一:我们可以使用多线程,将上面同步请求的代码放在多线程环境下,这样就不会阻塞主线程

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self performSelectorInBackground:@selector(connection) withObject:nil];
}

-(void)connection {
    //访问的URL
    NSString *str = @"http://localhost/iostest.php";
    //对URL进行编码
    str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    //设置url
    request.URL = [NSURL URLWithString:str];
    //缓存策略
    request.cachePolicy = 0;
    //设置请求方式
    request.HTTPMethod = @"POST";
    //设置请求体
    request.HTTPBody = [@"name=zanglitao&gender=male" dataUsingEncoding:NSUTF8StringEncoding];
    
    
    NSURLResponse *resp = [[NSURLResponse alloc] init];
    NSError *error = [[NSError alloc] init];
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&resp error:&error];
    
    NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}

输出:zanglitao|male

 

方式二:使用NSURLConnection的类方法sendAsynchronousRequest

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //访问的URL
    NSString *str = @"http://localhost/iostest.php";
    //对URL进行编码
    str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    //设置url
    request.URL = [NSURL URLWithString:str];
    //缓存策略
    request.cachePolicy = 0;
    //设置请求方式
    request.HTTPMethod = @"POST";
    //设置请求体
    request.HTTPBody = [@"name=zanglitao&gender=male" dataUsingEncoding:NSUTF8StringEncoding];
    
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    
}

 

方式三:通过NSURLConnectionDataDelegate代理实现,这种方式较为繁琐,优点是可以获得请求进度

@interface ZLTViewController () {
    NSMutableData *_data;
    int _length;
}

@end

@implementation ZLTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    //访问的URL
    NSString *str = @"http://g.hiphotos.baidu.com/image/h%3D800%3Bcrop%3D0%2C0%2C1280%2C800/sign=eaef18bb830a19d8d403890503c1e1f9/bd315c6034a85edfabe1bf294a540923dd54754e.jpg";
    //对URL进行编码
    str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:str]];

    //初始化NSURLConnection时设置代理对象
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    //开始请求
    [conn start];
}

//获得请求
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    _data = [[NSMutableData alloc] init];
    NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
    
    //取得响应头
    NSDictionary *headFields = [resp allHeaderFields];//取得响应数据长度
    _length = [headFields[@"Content-Length"] integerValue];
    NSLog(@"请求数据大小:%ld",_length);
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_data appendData:data];
    float percent = (float)[_data length]/_length * 100;
    NSLog(@"当前进度:%f",percent);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"请求结束");
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"请求失败");
}

以上代码访问了互联网的一张图片,当获得响应时didReceiveResponse会被触发,我们可以在这里通过Content-Length字段获取响应数据的大小,然后接收数据时didReceiveData会不断被调用

输出:

请求数据大小:6542

当前进度:59.202080

当前进度:79.807404

当前进度:100.000000

请求结束

 

下载文件

当文件很大的时候我们使用NSURLConnection直接下载是不现实的,常用方法是每次请求一小段数据,一次一次的下载

请求头中有个参数Range可以用来控制访问的数据的范围,比如100个字节的数据,我们可以通过设定Range:Bytes=20-80 下载20到80的字节(使用Range我们也可以实现多线程下载)

除了GET和POST,NSURLRequest还提供了HEAD的请求方式,可以仅请求头信息来获取数据的长度

注意:NSURLRequest不能使用缓存,因为我们会多次请求相同的url,如果使用了缓存每次都会请求第一次请求的数据

@interface ZLTViewController () {
    //储存请求来的数据
    NSMutableData *_data;
    //数据总长度
    int _length;
}

@end

@implementation ZLTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    _data = [NSMutableData data];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self download];
    });
}

//下载
-(void)download {
    long long length = [self getFileTotalLength];
    
    long long start = 0;
    long long end = 0;
    
    while (start < length) {
        end = start + 1024 - 1;
        if (end >= length) {
            end = length - 1;
        }
        [self downFilefrom:start end:end];
        NSLog(@"%lld,%lld,%lld",length,start,end);
        start += 1024;
    }
    
    
    __weak UIView *weakView = self.view;
    dispatch_async(dispatch_get_main_queue(), ^{
        UIImage *image = [UIImage imageWithData:_data];
        UIImageView *imageview = [[UIImageView alloc] initWithImage:image];
        [weakView addSubview:imageview];
    });
}


//获得文件url
-(NSURL *)getURL {
    NSString *urlStr = @"http://g.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a290bed8dc49a25bc315c607c96.jpg";
    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    return [NSURL URLWithString:urlStr];
}

//获取文件大小
- (long long)getFileTotalLength {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[self getURL]];
    request.HTTPMethod = @"HEAD";
    
    NSURLResponse *response = [[NSURLResponse alloc] init];
    NSError *error = [[NSError alloc] init];
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
    return response.expectedContentLength;
}

//下载指定区域的文件
- (void)downFilefrom:(long long)start end:(long long)end {
    NSString *range = [NSString stringWithFormat:@"Bytes=%lld-%lld",start,end];
    
    //缓存策略需要使用NSURLRequestReloadIgnoringCacheData,否则每次都会请求第一次请求的数据(0-1023)
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[self getURL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0f];
    [request setValue:range forHTTPHeaderField:@"Range"];
    
    
    NSURLResponse *response = [[NSURLResponse alloc] init];
    NSError *error = [[NSError alloc] init];
    
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
    //数据写入文件
    [self appendDataToFile:data];
    //数据写入NSData
    [_data appendData:data];
}

-(void)appendDataToFile:(NSData *)data {
    NSString *url = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    url = [url stringByAppendingPathComponent:@"1.jpg"];
    
    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:url];
    if (handle) {
        [handle seekToEndOfFile];
        [handle writeData:data];
        [handle closeFile];
    } else {
        [data writeToFile:url atomically:YES];
    }
}

@end

 

 

上传文件

首先我们看看使用浏览器上传文件时请求头

这里有两个关键的字段:Content-Length和Content-Type

Content-Length表示请求体字节长度,Content-Type前面是固定格式multipart/form-data; boundary=---------------------------,后面那串数字由浏览器随机生成最为boundary,我们也可以指定为任意的字符串值

 

请求体:

请求体开始是--加上请求头中boundary=后面的内容,然后跟着一个\n换行

第二行中name="file"表示表单中文件使用了file这个name(所以服务器需要通过file接收数据) filename指定了上传文件的文件名,然后跟着一个\n换行

第三行表示上传文件的MIMETYPE,最后跟着两个\n(如果漏了一个\n会上传失败)

第五行开始是文件的内容,文件结束后跟着一个\n(最容易遗忘的一个\n)

最后是-- + 请求头中boundary=后面的内容 + --,不要忘了后面还有两个\n

 

所以我们写代码时主要注意点在两处,一个是设置请求头的两个参数,一个是请求体的拼接,具体代码如下

//自定义一个boundary
#define boundary @"zanglitao"
@interface ZViewController()
@end

@implementation ZViewController

-(void)viewDidLoad {
    [super viewDidLoad];

    [self upload];
}

-(void)upload {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://localhost/iostest.php"]];
    //必须使用POST
    request.HTTPMethod = @"POST";
    
    NSData *data = [self getDataBody];
    //设置请求头
    [request setValue:[NSString stringWithFormat:@"%d",data.length] forHTTPHeaderField:@"Content-Length"];
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary] forHTTPHeaderField:@"Content-Type"];
    //设置请求体
    request.HTTPBody = data;
    
    
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (connectionError) {
            NSLog(@"%@",[connectionError localizedDescription]);
        } else {
            NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        }
        
    }];
}

//获取请求体内容
-(NSData *)getDataBody {
    NSMutableData *data = [NSMutableData data];
    
    NSString *top = [NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"file\"; filename=\"1.png\"\nContent-Type: image/png\n\n",boundary];
    
    NSString *bottom = [NSString stringWithFormat:@"\n--%@--\n\n",boundary];
    
    NSData *content = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"]];
    
    [data appendData:[top dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:content];
    [data appendData:[bottom dataUsingEncoding:NSUTF8StringEncoding]];
    return data;
}

@end

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。