• 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=后面的内容,然后跟着一个 换行

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

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

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

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

     

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

    //自定义一个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:@"--%@
    Content-Disposition: form-data; name="file"; filename="1.png"
    Content-Type: image/png
    
    ",boundary];
        
        NSString *bottom = [NSString stringWithFormat:@"
    --%@--
    
    ",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
  • 相关阅读:
    Android应用增量更新
    corner2
    UIProject
    Argo
    flower-recognition
    Android-Chat-Widget
    leanchat-android
    Beautyacticle
    XianBicycle
    完整性约束(章节摘要)
  • 原文地址:https://www.cnblogs.com/zanglitao/p/4072229.html
Copyright © 2020-2023  润新知