zoukankan      html  css  js  c++  java
  • NSURLConnection、NSURLSession

    NSURLConnection
     
    1、准备网络资源地址:URL
    注意:由于URL支持26个英文字母,数字和少数的几个特殊字符. 因此对于URL中包含非标准URL的字符,需要进行编码. iOS提供了函数 stringByAddPercentEscapesUsingEncoding对中文和一些特殊字符进行编码
    2、发送数据请求
    3、遵守协议:<NSURLConnectionDataDelegate>,处理下载任务时的事件
     
    @interface ViewController ()<NSURLConnectionDataDelegate>
    //用于存储网络下载的数据.把分包的数据整合起来.
    @property (nonatomic, strong) NSMutableData *receivedData;

    @end
    //!!请注意URL地址必须包含http等协议的前缀. xcode并不会自动为我们添加.
    //    NSURL OC的URL类型,地址资源定位
        NSURL *url = [NSURL URLWithString:@"http://www.tmooc.cn"];
    // OC的请求类型,包含了请求相关的操作.例如缓存.cookie等等...
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //    Immediately 立刻,马上
    //    startImmediately 立刻开始
    //    YES代表立刻开始请求,
    //    NO 不需要立刻开始,需要手动触发.
        NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    //    手动开始请求
        [conn start];
    }
     
    //1、当收到服务器的请求响应,触发此代理
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
        NSLog(@"didReceiveResponse:%@", response);
        _receivedData = [[NSMutableData alloc] init];
    }
    //2、收到服务器传输的数据,如果数据量比较大的话,服务器会分批传,俗称数据包.
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    //    由于data有可能是分批的,所以我们需要一个全局的MutableData去拼接起来
        NSLog(@"didReceiveData:%ld个字节", data.length);
        [_receivedData appendData:data];
    }
    //3、连接成功被加载了
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection{
        NSLog(@"connectionDidFinishLoading");
    //    写到沙盒的document文件夹下的baidu文件中
        NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/baidu"];
        NSLog(@"path is %@", path);
    //    data类型转换为字符串,再存储
    //    使用NSUTF8StringEncoding编码类型,对_receivedData这个二进制类型进行转码,转换为字符串
        NSString *baiduStr = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
    //    定义一个error指针,不需要初始化
    //    error指针的初始化由下面的方法来完成. 如果有错误就会被赋值. 没错误,依然会是nil
        NSError *error = nil;
        [baiduStr writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&error];
        if (error) {
    //        error有值,则说明上面的写文件方法出错了
            NSLog(@"error:%@", error);
        }
    }
     
    //——————————————————--------------------—数据请求的方式----------------------------------------
    //通常我们使用宏定义 或者 static的方式 在文件的顶端,声明文件中需要用到的常量
    //好处:便于维护. 特别是一个常量 要很多个位置使用时
    //习惯上,添加k为前缀
    #define kBaiduURLString    @"http://www.baidu.com"
     
        [selfsendSyncRequest:kBaiduURLString];
     
    异步请求:(在子线程执行)
    - (void)sendAsyncRequest:(NSString *)urlString{
    //转码: 对urlString进行标准化的编码,防止中文和特殊字符的出现
    //    Percent 百分号,分数
        urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSURL *url = [NSURL URLWithString:urlString];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        NSLog(@"主线程是%@", [NSThread mainThread]);
    //异步请求方法:
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    //下载结束后的处理:
            NSLog(@"在线程%@中处理",[NSThread currentThread]);
            NSLog(@"接收到%ld字节的数据", data.length);
        }];
    }
     
    同步请求:(不用,在主线程执行)
        urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSURL *url = [NSURL URLWithString:urlString];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //    用于存储服务器返回的响应,参考delegate方式中的didReceiveResponse
        NSURLResponse *response = nil;
        NSError *error = nil;
    //    发送同步请求
        NSTimeInterval beginTime =[NSDate timeIntervalSinceReferenceDate];
        NSData *receivedData =[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        NSLog(@"接收到%ld个字节的数据", receivedData.length);
        NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate];
        NSLog(@"请求耗时%f秒", endTime - beginTime);
    //——————————————————------------------------—End--------------------------------------------
     
    //———————————————————-------------------断点下载----------------------------------------------
    服务器: 被动的角色
    客户端: 主动的角色
    客户端必须告诉服务器当前应该从哪个地方开始继续下载

    如何告诉, 通过http协议的数据包?
    客户端 —http> 服务器
    key(BigFile.zip: 600M):
    1) 客户端Request请求头中Range: bytes=100-200 ;—> 服务器从100bytes开时发送给客户端, 直到200bytes
    2) 客户端Request请求头中Range: bytes=100- ;—> 服务器从100bytes开时发送给客户端, 直到发送完毕为止
    步骤:
    1. 使用NSFileManager在didReceiveResponse方法中创建一个空的文件(在沙盒中的caches路径下),获取文件的总大小
    2. 在didReceiveData方法中: 首先将writeHandler移到文件的最后, 之后再将数据写入文件中.
    3. 在DidFinishLoading方法中, 对属性重新初始化,在关闭文件写句柄
     
    //Caches路径
    @property(nonatomic,strong)NSString *cachePath;
    @property(nonatomic,strong)NSFileHandle *writeHandle;
    @property(nonatomic,assign)long long currentLength;//已下载的文件的大小(以字节为单位)
    @property(nonatomic,assign)long long totalLength;//文件的总大小
    //断点传输
    @property(nonatomic,strong)NSMutableURLRequest *request;//要使用可变的请求对象
    @property(nonatomic,strong)NSURLConnection *conn;

    @end
    @implementation ViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.cachePath=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject];
     
    }
    - (IBAction)downloadBigFile:(UIButton *)sender {
        NSURL *url=[NSURL URLWithString:@"http://localhost:80/Evernote.zip"];
     
        self.totalLength=0;
        self.currentLength=0;
        self.request=[NSMutableURLRequest requestWithURL:url];
        //设置range:bytes=0-
        NSString *range= [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
        [self.request setValue:range forHTTPHeaderField:@"Range"];
        self.conn = [NSURLConnection connectionWithRequest:self.request delegate:self];
    }
    - (IBAction)cancelDownLoad:(UIButton *)sender {
        //取消下载
        [self.conn cancel];
        self.conn=nil;
    }
    - (IBAction)resumeDownLoad:(UIButton *)sender {
        //恢复下载
        //Range:bytes=
        NSString *range=[NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
        [self.request setValue:range forHTTPHeaderField:@"Range"];
        self.conn = [NSURLConnection connectionWithRequest:self.request delegate:self];
    }
    //客户端请求成功,接收到响应response
    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    //获取返回的代号,判断出服务器响应的结果
        NSInteger status=[(NSHTTPURLResponse *)response statusCode];
        NSLog(@"status:%ld",(long)status);
        if (status == 206) {
            //在沙盒Caches/文件夹下,创建出用来存储数据的文件
            NSFileManager *manage=[NSFileManager defaultManager];
            NSString *fliePath=[self.cachePath stringByAppendingPathComponent:@"text.zip"];
            NSLog(@"写入的文件路径:%@",fliePath);
            [manage createFileAtPath:fliePath contents:nil attributes:nil];
            //获取文件的总大小(为了显示进度)
            self.writeHandle=[NSFileHandle fileHandleForWritingAtPath:fliePath];
            if (self.currentLength ==0) {//服务器返回文件的剩余大小,因此要获取一次返回的大小
                self.totalLength=response.expectedContentLength;
            } 
            NSLog(@"文件的总大小:%lld",self.totalLength); 
        }
    }
    //接收到数据
    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
        //当前接收数据包的大小
        static int i=0;
        i++;
        NSLog(@"当前接收数据大小_%d:%ld",i,(unsigned long)data.length);
        //移动到文件尾部
        [self.writeHandle seekToEndOfFile];
       //写入文件
        [self.writeHandle writeData:data];
        self.currentLength +=data.length;
        NSLog(@"总接收数据:%lld",self.currentLength);
        self.progressView.progress = (double)self.currentLength / self.totalLength;
        NSLog(@"当前进度:%f",self.progressView.progress);
    }
    //下载完成后,关闭句柄
    -(void)connectionDidFinishLoading:(NSURLConnection *)connection{
        [self.writeHandle closeFile];
    }
    //错误处理
    -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
        NSLog(@"error : %@",[error userInfo])
    }
     
    //*********************************************
    苹果公司在iOS7,给SDK中添加了新的网络处理类:NSURLSession
    它与URLConnection是并列关系.
    URLSession包含了URLConnection的所有功能, 额外还添加了以下4点特性:
    1.通过URL将数据下载到内存
    2.通过URL将数据下载到文件系统
    3.将数据上传到指定的URL
    4.在后台完成上述的操作
    适用场景:     ios7+; 封装很多实现细节;
    使用NSURLSession之前
    选择工作模式:
    session(会话)三个类型:
    session1  <- defaultSessionConfiguration:(默认模式),类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权
    session2  <- ephemeralSessionConfiguration:(瞬时模式),不会在磁盘中保存任何数据,所有与会话有关的caches、证书、cookies等都保存在RAM中,因此当程序会话无效时,这些缓存数据就会被自动清理。
    session3  <- backgroundSessionConfiguration:(后台模式),在后天完成上传和下载,在创建congfiguration对象的时候需要提供一个NSString类型的ID用来标示完成工作的后台会话。

    task三个类型:
    1、NSURLSessionDataTask:
         处理小数据相关的任务(不包括下载和上传)
         例子: 返回json数据; 返回html文件(放在UIWebView);
                   返回xml数据
    2、NSURLSessionDownloadTask:
    //采用的是子线程下载
         主要用于下载任务; delegate方法接收或者处理服务器返回的数据
    3、NSURLSessionUploadTask:
         主要用于上传任务;
    后台运行:
    主要configuration的配置: backgroundSessionConfiguration
    获得NSURLSession对象:
    第一种方式是使用静态的sharedSession方法,该类使用共享的会话,该会话使用全局的Cache,Cookie和证书。
    第二种方式是通过sessionWithConfiguration:方法创建对象,也就是创建对应配置的会话,与NSURLSessionConfiguration合作使用。
    第三种方式是通过sessionWithConfiguration:delegate:delegateQueue方法创建对象,二三两种方式可以创建一个新会话并定制其会话类型。该方式中指定了session的委托和委托所处的队列。当不再需要连接时,可以调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。
     
    //---------------------------------使用步骤--------------------------------------------------------
    1、准备网络资源地址URL、发送请求NSURLRequest
    2、创建NSURLSession对象
    3、执行下载任务NSURLSessionDownloadTask
    4、下载完成后,对文件的处理
    NSURLSessionDownloadTask  *downloadTask=[session downloadTaskWithRequest:request
                        completionHandler:^(NSURL *location,//location是位于沙盒中的tmp目录下,要转移(会被定时清理的) 
                        NSURLResponse *response,//服务器的响应
                        NSError *error){
              });
    //------------------------------------------------------------------------------------------------
    //————————————————-----------—————简单的使用方式-------------------------------------------
     
    - (IBAction)downLoad:(UIButton *)sender {
        //1创建NSURLSession对象
        NSURLSession *session=[NSURLSession sharedSession];//使用默认的session(回话)方式
        NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=298400068,822827541&fm=116&gp=0.jpg"]];
        NSURLSessionDownloadTask  *downloadTask=[session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
            NSLog(@"下载完成:%@",[NSThread currentThread]);//子线程
           
            NSInteger status=[(NSHTTPURLResponse *)response statusCode];//服务器返回的编号
            NSLog(@"status:%ld",(long)status);
            if (status ==200) {
                NSLog(@"location:%@",location);
    //location是位于沙盒中的tmp目录下,要转移(会被定时清理的)
    //要将文件转移到Caches中
                //1、获取路径
                NSLog(@"response : %@",response);
                NSString *path=[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject]stringByAppendingPathComponent:@"mm.jpg"];
                NSLog(@"path :%@",path);
                //2、移动文件
                [[NSFileManager defaultManager]moveItemAtPath:location.path toPath:path error:nil];
                //显示图片
                UIImage *image=[UIImage imageNamed:path];
    //4.回到主线程将图片显示在界面上,为什么要回到主线程?答:因为视图层都在主线程中
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.imageView.image=image;
                });
            }
        }];
        //执行下载任务
        [downloadTask resume];
    }
    //------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------
  • 相关阅读:
    苹果一体机发射Wi-Fi
    iphone 屏蔽系统自动更新,消除设置上的小红点
    data parameter is nil 异常处理
    copy与mutableCopy的区别总结
    java axis2 webservice
    mysql 远程 ip访问
    mysql 存储过程小问题
    mysql游标错误
    is not writable or has an invalid setter method错误的解决
    Struts2中关于"There is no Action mapped for namespace / and action name"的总结
  • 原文地址:https://www.cnblogs.com/lignpeng/p/5458401.html
Copyright © 2011-2022 走看看