NSURLSession

作者: 彼岸的黑色曼陀罗 | 来源:发表于2016-10-13 21:28 被阅读96次

    NSURLSession基本使用

    简介

    • 使用步骤
      • 使用NSURLSession会话对象创建Task,然后执行Task
      • Task类型
        • NSURLSessionTask
          • NSURLSessionDataTask(GET/POST)
            • NSURLSessionUploadTask(文件上传)
          • NSURLSessionDownloadTask(文件下载)
    • 获得NSURLSession
      • 方法一:获得共享的Session
        • sharedSession
      • 方法二:自定义Session
        • sessionWithConfiguration:delegate:delegateQueue:
    • 创建NSURLSessionTask

    如何发送GET请求

    方法一
    • 01.确定请求路径NSURL
    • 02.创建请求对象NSURLRequest
    • 03.创建|获取会话对象
      • sharedSession
      • 自定义session
    • 04.根据会话对象来创建请求任务
      • dataTaskWithRequest:
        • 参数一:请求对象
        • 参数二:completionHandler完成之后调用的代码块
          • data:响应体
          • response:响应头
          • error:错误信息
    • 05.执行任务(发送请求)
      • [dataTask resume];
        • resume:NSURLSession创建好之后默认是挂起状态需要调用resume恢复
    • 06.解析数据
      • block块默认情况是在子线程中执行的!!!
    • 修改配置文件:ATS
    方法二
    • 确定请求路径
    • 创建会话对象
    • 创建task
      • dataTaskWithURL:completionHandler: 该方法内部会根据传入的URL自动创建一个请求对象(默认是GET请求)
    • 发送请求,执行任务resume
    • 解析数据
    方法三
    [[NSURLSession sharedSession]dataTaskWithURL:[NSURL URLWithString:]completionHandler:{解析数据}]resume]
    

    如何发送POST请求

    • 01.确定请求路径NSURL
    • 02.创建可变的请求对象NSMutableURLRequest
    • 03.修改请求方法为POST
      • request.HTTPMethod = @"POST"
    • 04.设置参数(设置请求体)
      • 把参数转化成二进制数据dataUsingEncoding
      • .HTTPBody
    • 05.创建会话对象
      • Configuration:配置信息
      • sessionWithConfiguration:
    • 06.创建task
      • dataTaskWithRequest:
    • 07.发送请求resume
    • 08.解析数据

    代理方法的使用

    • 确定请求路径
    • 创建请求对象
    • 创建|获取会话对象
      • 自定义session
      • sessionWithConfiguration:delegate:delegateQueue:
        • 参数一:配置信息defaultSessionConfiguration默认
        • 参数二:谁成为session的代理
        • 参数三:队列->决定代码块(所有的代理方法)在哪个线程中调用 [NSOperationQueue mainQueue]代理方法就在主线程中,如果队列为nil,那么代理方法在子线程中调用
    • 创建task
      • dataTaskWithRequest:
    • 发送请求resume
    • 遵守协议<NSURLSessionDataDelegate>
    • 代理方法
      • 当接收到服务器响应的时候调用didReceiveResponse:completionHander:
        • 需要告诉系统应该如何处理服务器返回的数据block块是需要我们回调的
        • completionHander(NSURLSessionResponseAllow)
          • NSURLSessionResponseAllow需要手动告诉系统允许
          • NSURLSessionResponseCancel默认
      • 当接收到服务器返回的数据(响应体)的时候调用,该方法可能会调用多次didReceiveData:
        • 需要搞一个可变的二进制数据进行拼接
        • self.data appendData:
      • 当请求完成的时候或者出错的时候调用didCompleteWithError:
        • 解析数据

    登录小案例

    • 用户名textField
    • 密码textField
    • 登录按钮
      • 发送POST请求(用户名和密码,POST请求安全性高)
      • 得到用户的输入
        • usernameTextF.text
        • pwdTextF.text
      • 确定请求路径
      • 创建可变的请求对象NSMutableURLRequest:
      • 修改请求方法为POST
        • .HTTPMethod
      • 设置参数
        • stringWithFormat:拼接参数
        • 把字符串转为二进制数据
        • .HTTPBody
      • 创建会话对象
      • 弹出信息,提示用户正在加载
        • SVProgressHUD showStatus:
      • 创建task
      • 发送请求resume
      • 解析数据initWithData:encoding:
        • HUD延迟执行dismiss
        • HUD反馈信息给用户showSuccessWithStatus:
        • 用户名不存在|密码不正确|登录成功
          • 字符串里是否有error|success
          • containsString:
          • showError|showSuccess
          • 使用字符串之前,会对字符串进行解析,怎么把字符串转成字典?
            • JSON解析
      • shift+command+k退出键盘
    • 代码完善
      • 用户没有任何输入,点击按钮发送了网络请求
        • 正则表达式
        • 校验用户是否有输入,如果用户名密码为空-不能登录
      • 弹出提示框
        • 第三方框架SVProgressHUD
        • showErrorWithStatus:占位文字
      • 取消弹框
        • dispatch_after延迟执行dismiss
      • 发送网络请求的时候弹出提示框,提示用户正在加载
      • 显示遮盖蒙板,不能操作界面
        • SVProgressHUD setDefaultMaskType:
      • 数据解析JSON->OC
        • 最外层{}
        • NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:options:error:]
        • 显示HUD提示信息的时候就可以直接根据key进行取值,不用截取字符串了!

    数据解析

    JSON

    • 一种轻量级的数据格式,一般用于数据交互
    • 服务器返回给客户端的数据,一般都是JSON或者XML格式(文件下载除外)
    • 格式很像OC中的字典和数组
      • {"name":"jack","age":10}
      • {"name":["jack","rose","jim"]}
    • 标准的JSON格式的注意点:key必须用双引号
    • JSON转为OC数据
      • {}-->NSDictionary
      • []-->NSArray
      • ""-->NSString
      • 数字-->NSNumber
      • 从外向内转换
    • JSON解析方案
      • 第三方框架:JSONKit/SBJson/TouchJSON(性能从左到右,越来越差)
      • 苹果原生:
        • NSJSONSerialization(性能最好)JSON的序列化处理
        • JSON-->OC
          • JSONObjectWithData:
        • OC-->JSON
          • dataWithJSONObject:
    JSON反序列化处理(JSON->OC)
    • 确定请求路径NSURL
    • 创建请求对象NSURLRequest
    • 创建会话对象NSURLSession
    • 创建task
      • dataTaskWithRequest:completionHander:
    • 执行任务resume
    • 解析数据
      • 解析JSON数据(data本质上是一个JSON数据)
      • NSJSONSerialization JSONObjectWithData:options:error:
        • 参数一:要解析的JSON数据
        • 参数二:选项,枚举[3]
          • MutableContainers生成的字典或者数组是可变的
          • MutableLeaves 生成的字典中的(key)字符串也是可变的
          • AllowFragments 最外层既不是字典也不是数组
          • 默认做法是传0,kNilOptions
        • 参数三:错误信息,传地址
        • 返回值:是一个对象
      • 把一个JSON字符串转成OC的数据类型(字典|数组|其他)
    JSON序列化处理(OC->JSON)
    • @{@"name":@"xiaomage"}字典

    • @[@"name",@"age"]数组

    • 字典和数组都可以转成JSON,但是字符串不能转为JSON;

    • NSJSONNSerialization dataWithJSONObject:options:error:

      • 参数一:要转化的OC对象
      • 参数二:选项 NSJSONWritingPrettyPrinted 排版
      • 参数三:错误
      • 返回值:JSON二进制数据
    • 哪些对象可以转为JSON呢?

      • 通过[NSJSONSerialization isValidJSONObject:]判断
      • 需要满足的条件
        • 最外层必须是字典或者是数组
        • 所有的对象必须是字符串、NSNumber、NSArray/NSDictionary、NSNull中的一种
        • 字典的key都必须是NSString
        • NSNumber是标准的,并且不是无穷大
    OC和JSON转换的注意点:
    • "false"-->NSNumber 0

    • "true" -->NSNumber 1

    • "null" -->NSNull 空

    • 字典不能传nil,但是@{@"name":[NSNull null]}这样就不会报错

    • 把.plist里面的数据,以JSON的形式进行存储

      • arrayWithContentsOfFile:
      • NSJSONSerialization dataWithJSONObject:options:NSJSONWritingPrettyPrinted error:
      • writeToFile:atomically:
    • 怎么取JSON数据?

      • dataWithContentsOfFile:
      • 解析JSON,转换成OC对象
      • NSJSONSerialization JSONObjectWithData:options:error:
    复杂JSON解析-数据展示
    • tableViewController
    • sytle:subtitle
    • indentifier:
    • 发送网络请求获取数据
    [[ NSURLSession sharedSession]dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^{
    解析服务器返回的数据
    NSDictionary * dict = [NSJSONSeriazlization JSONObjectWithData:options:error:] 
    方法一:写成plist文件
    [dict writeToFile:atomically:]
    得到字典数组
    NSArray *array = [dict objectForKey:]
    self.videos = array ;
    //回到主线程显示数据
    dispatch_async(dispatch_get_main_queue(),^{
    刷新tableView
    [self.tableView reloadData]
    });
    }] resume]; 
    
    • 复杂的JSON数据如何处理?

      • 01.写plist文件
      • 02.在线格式化JSON数据
        • 百度JSON在线格式化tool.oschina
    • 展示数据

      • 获得cell
      • 设置cell的数据
        • 得到该行cell对应的数据
        • 设置标题、子标题
        • 图片url路径不完整,需要拼接主机地址
          • 多图下载SDWebImage
          • UIImageView+WebCache
          • 拼接主机地址
          • sd_setImageWithURL:placeholderImage:
          • 图片尺寸问题:设置frame
      • 返回cell
    复杂JSON解析- 播放视频
    • 点击cell,创建一个播放器播放视频
    • didSelectRowAtIndexPath:
      • 获得该cell对应的数据dict= self.videos[indexPath.row]
      • 拼接路径
      • 创建一个视频播放控制器
        • 导入一个库:import<MediaPlayer/MediaPlayer.h>
        • MPMoviePlayerViewController alloc initWithContentURL:
      • 弹出控制器
        • modal
        • [self presentViewController:animated:completion:]
    复杂JSON解析 - 面向模型开发
    • 创建模型
    • 字典转模型
    • 字典数组转为模型数组
    • 问题:模型里面的属性id的颜色是关键字颜色,把id替换为ID?
      • 可以使用框架MJExtension
      • 在字典转模型之前,告诉框架应该用什么属性来替换掉字典中的某个key
     [模型名称 mj_setupReplaceKeyFromPropertyName:^NSDictionary*{
        return @{@"ID":@"id"};
            }] 
    
    • 字典转模型框架
      • Mantle:需要继承自MTModel
      • JSONModel:需要继承自JSONModel
      • MJExtension :不需要继承、无代码侵入性

    XML(不需要掌握)

    • 学习网站:W3school.com
    • XML文档组成:
      • 文档声明:<?xml version = "1.0"?>
      • 元素Element
        • 开始标签
        • 结束标签
        • 拥有内容的元素:<video>小黄人</video>
        • 没有内容的元素:<video></video>
        • 没有内容的元素简写:<video/>
        • 一个元素可以嵌套若干个子元素(不能出现交叉嵌套)
        • 注意:所有的空格和换行,都会当做具体内容处理
      • 属性Attribute
        • 一个元素里面可以拥有多个属性
        • <video name = "小黄人 " length = "30" />
        • video元素拥有name和length两个属性
        • 属性值必须用“”或者‘’括住
        • 实际上,属性表示的信息也可以用子元素来表示
    使用NSXMLParser解析XML
    • 提取video元素中的name和length属性的值

    • XML的解析方法有两种

      • DOM:一次性的将整个XML文档加载进内存,比较适合解析小文件
      • SAX:从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件
    • ios开发中解析XML的方式

      • 苹果原生NSXMLParser:SAX方式解析,使用简单
      • 第三方框架
        • libxml2:纯C语言,默认包含在ios sdk中,同时支持DOM和SAX方式解析
        • GDataXML:DOM方式解析,由Google开发,基于libxml2
    • XML解析方式的选择建议:

      • 大文件:NSXMLParser/libxml2
      • 小文件:GDataXML/NSXMLParser/libxml2
    • NSXMLParser

      • 使用步骤
        • 传入XML数据,创建解析器 NSXMLParser * parser = [[NSXMLParser alloc]initWithData:data];
        • 设置代理,监听解析过程parser.delegate = self;
        • 开始解析 [parser parse]
          • 该方法本身是阻塞式的,同步
        • 遵守协议<NSXMLParserDelegate>
        • 实现代理方法
          • 开始解析XML文档的时候调用parserDidStartDocument:
          • 开始解析某个元素的时候调用,该方法会调用多次didStartElement:namespaceURI:qualifiedName:attributes:
            • 第二个参数:元素的名称
            • 最后一个参数:属性字典
            • 字典转模型,把模型放到数组里面-->模型数组,作为tableView的数据源
              • 需要做过滤处理:如果是根元素,就直接返回,不进行这一个操作
          • 某个元素解析完毕的时候调用,该方法会调用多次parser:didEndElement:namespaceURI:qualifiedName:
          • 整个XML文档解析完毕的时候调用 parserDidEndDocument:
      • JSON和XML本质上就是字符串(有特殊格式的字符串)
    使用GDataXML解析XML
    • GDataXML框架
    • 解析数据之前要检查数据获取有没有问题
    • 配置
      • 导入libxml2(现在默认包含了这个库)
      • 设置libxml2的头文件搜索路径(为了找到libxml2库的所有头文件)
        • 在Head Search Path中加入/usr/include/libxml2
      • 设置链接参数(自动链接libxml2库)
        • 在Other Linker Flags中加入 -lxml2
    • 非ARC
      • 告诉编译器在编译的时候以非ARC进行编译
        • -fno-objc-arc
    • 解析数据
      • 加载整个XML文档
        • GDataDocument alloc initWithData:options:error:
      • 得到根元素,根据根元素得到内部所有名称为video的子元素
        • [.rootElement elementsForName:]返回值是数组
      • 遍历所有的子元素,得到子元素中的属性
        • for in
        • 元素的类型:GDataXMLElement
        • 创建一个空的模型
        • 给模型里的属性赋值
        • video.ID = [element attributeForName:@"id"].stringValue;
        • addObject:把模型添加到可变数组中

    JSON和XML比较

    • 同一份数据,既可以用JSON来表示,也可以用XML来表示
    • JSON更加轻量级
    • JSON的体积小于XML,所以服务器返回给移动端的数据以JSON居多

    文件下载

    小文件下载

    • 文件下载的第一种方式:NSData dataWithContentsOfURL:
    • 局限性:
      • 只能处理小文件,如果文件过大会造成内存飙升
      • 不能监听下载进度
      • 性能不好
      • 一般开发中不用这种方法下载文件,以后尽量不要使用了

    NSURLSessionDataTask下载文件

    NSURLSession发请求下载文件(DataTask-代理)
    • 确定请求路径
    • 创建请求对象
    • 创建会话对象NSURLSession
      • sessionWithConfiguration:delegate:delegateQueue:
    • 遵守代理协议<NSURLSessionDataDelegate>
    • 实现代理方法
      • 接收到服务器响应的时候调用didReceiveResponse:completionHandler:
        • 告诉服务器怎么处理服务器返回的数据completionHandler(NSURLSessionResponseAllow)
      • 接收到服务器返回的数据的时候调用 didReceiveData:
        • 搞一个可变的data,初始化处理(或者懒加载),不断拼接服务器返回的数据 appendData:
      • 整个请求完成或者失败的时候调用didCompleteWithError:
        • 处理拼接得到的数据(保存到沙盒里面)
          • 获得caches路径
            • NSSearchPathForDirectoriesInDomains()
          • 设置文件的名称
            • NSString *fileName = task.response.suggestedFileName
            • suggestedFileName:推荐的文件名称
          • 拼接获得全路径
            • stringByAppendingPathComponent:
          • 写数据到磁盘
            • writeToFile:atomically:
    • 创建请求任务
      • session dataTaskWithRequest:
    • 执行task
      • [dataTask resume]
    监听文件下载进度
    • 下载进度 = 当前已经下载的数据大小/文件的总大小
    • 如何获得当前已经下载的数据大小?
      • 第一种方法:self.fileData.length
      • 第二种方法:不断累加data的大小
    • 如何获得文件的总大小?
      • 通过响应头来得到
      • 在didReceiveResponse:方法里面拿响应头
        • 得到本次请求的文件数据大小response.expectedContentLength;
      • 在didReceiveData:方法里面计算进度
        • 1.0*self.fileData.length/self.tatalSize
    • 问题:内存飙升的问题!!
      • 原因:fileData属性变量占用内存空间
      • 解决:把拿到的每一块数据直接写入到沙盒(边接受数据边把数据写入到沙盒)
    解决内存飙升的问题- 文件句柄指针
    • 01.在沙盒里面创建一个空的文件
      • 在didReceiveResponse:方法里
        • 获得caches路径
        • 设置文件的名称
        • 拼接获得全路径
        • 创建空的文件[NSFileManager defaultManager]createFileAtPath:contents:attributes:
          • 参数一:要创建的文件保存的位置
          • 参数二:文件内容
          • 参数三:文件属性
    • 02.创建文件句柄指针,指向哪里就从哪里写,让文件句柄指针指向空的文件
      • [NSFileHandle fileHandleForWritingAtPath:]
    • 03.当接收到数据之后,使用文件句柄指针写数据,写一点句柄指针移动一点(边写数据边移动指针)
      • 定义一个NSFileHandle属性
      • [self.handle writeData:]
      • 累加当前已经下载的文件数据大小
        • self.currentSize +=data.length;
      • 计算文件的下载进度currentSize/totalSize
    • 04.所有文件写完之后,关闭文件句柄指针
      • 在didCompleteWithError:中关闭
      • [self.handle close]
    解决内存飙升的问题- 输出流的基本使用
    • 解决内存飙升问题:
      • NSFileHandle文件句柄指针
      • 输出流NSOutputStream
        • 创建输出流(路径)如果该路径对应的文件不存在那么会自动创建一个空的文件
        • 开始任务(打开输出流)
        • 当接收到数据之后使用输出流来写数据
        • 关闭输出流
    • 输出流的基本使用(水管)
      • 用输出流实现离线断点下载
      • 当接收到服务器响应的时候创建输出流
      • didReceiveResponse:
        • 创建输出流NSOutputStream alloc initToFileAtPath:append:
          • 参数一:文件的路径(全路径)
          • 参数二:是否追加拼接YES
        • 打开输出流,开始任务
          • [stream open]把服务器的数据引入到客户端
          • 定义一个输出流属性
        • 写数据 self.stream write:maxLength:
          • 参数一:传字节,data.bytes
          • 参数二:大小,data.length
        • 关闭输出流
          • [self.stream close]
    • 好处:不需要创建空的文件,也不需要判断
    常用操作
    • 定义一个请求任务属性,写一个懒加载
      • NSURLSessionDataTask 属性用strong修饰
        • 在cancel方法之后手动清空
    • 开始start
      • [self.dataTask resume]
    • 暂停suspend
      • [self.dataTask suspend]
      • 暂停是可以恢复的
    • 恢复resume
      • [self.dataTask resume]
    • 取消cancel
      • [self.dataTask cancel]
      • 手动给指针(NSURLSessionDataTask)清空
      • 取消不可以恢复
        • cancel代表整个请求已经结束了
      • 怎么实现点击取消可以恢复下载?
        • self.dataTask = nil
    文件断点下载
    • 应用场景
      • 当用户点击了取消之后想要继续下载文件(默认:从头开始把整个文件下载--缺点:浪费流量)
      • 实现条件
        • 知道应该下载哪一部分数据 currentSize
        • 如何实现只下载某个文件的一部分数据
    • 代码实现
      • 设置请求头信息,只请求某一部分数据

      • [NSString stringWithFormat:@"bytes=%zd-",sefl.currentSize]

      • [request setValue:forHTTPHeaderField:@"Range"]

      • bug:进度问题(进度不准确,进度信息变大了)

        • currentSize一直都没有改变
        • 重新调用didReceiveResponse代理方法,totalSize被重新赋值
        • 真正文件的总大小 = 当前已经下载的+本次请求的
        • totalSize = response.expectedContentLength+self.currentSize
      • bug:文件下载不完整,文件损坏

        • 重新发送网络请求,重新走了didRecevieResponse,又创建了一个空的文件,但是第一次已经创建了一个空的文件,怎么判断之前有文件了?
          • 判断currentSize是否为0,如果为0再创建空的文件,否则不再创建
      • bug:点击了取消之后,调用didCompleteError:handler被释放掉了,下载后面的数据的时候,重新创建了句柄指针,指向文件的开头,边写边移动,直接覆盖,怎么让句柄指针,让它指向文件的末尾,再让它写数据!

        • 让文件句柄指向文件的末尾[self.handle seekToEndOfFile]
    离线断点下载优化(进度条)
    • 当程序重新启动后显示实时的进度信息!!

    • 怎么做到实时显示数据?

    • 当计算文件总大小之后,把文件数据的总大小就保存到沙盒里去

      • 把文件的总大小包装成NSData
        • stringWithFormat:先转换成字符串
        • dataUsingEncoding:把字符串转为NSData
    • 计算进度信息 = 已经下载的数据大小 /文件的总大小

      • 加载文件总大小
        • dataWithContentsOfFile:
        • alloc initWithData:encoding:
        • .integerValue
        • 第一次程序启动的时候,文件总大小为0,需要做判断,不等于0的时候计算下载进度
    • 退出程序:shift+command+hh

    • 性能问题:

      • 定义一个NSURLSession属性,给session写一个懒加载
      • 开了很多的线程,NSURLSession内部会开很多子线程同时处理任务,所以使用NSURLSessionDataTask性能不好
    • NSURLSession使用的注意点:

      • 代理的问题:给NSURLSession设置代理,控制器没有办法释放
        • 问题演示:导航控制器里添加item,点击item,push控制器,重写控制器的dealloc方法,点击开始下载(设置了代理),dealloc方法没有调用,控制器没有被释放
        • 解决问题:
          • invalidateAndCancel马上退出
          • finishTasksAndInvalidate 下载运行完毕之后才退出
          • 在控制器即将消失的时候调用这两个方法,可以在viewDidDisappear方法里面调用

    NSURLSessionDownloadTask下载文件

    直接使用block下载文件
    • 确定请求路径

    • 创建请求对象

    • 创建会话对象

    • 根据会话对象创建一个downloadTask

      • NSURLSessionDownloadTask--6个方法
      • downloadTaskWithRequest:completionHandler:
        • 参数一:请求对象
        • 参数二:完成之后的回调
          • location:文件的临时存储路径,该方法内部会自动完成边接收数据边写沙盒的操作
          • response:响应头信息
          • error:错误信息
    • 发送网络请求resume

    • 当文件下载完毕之后,剪切文件到安全的位置(从临时路径转移到cache路径)

      • NSSearchPathForDirectoriesInDomains()
      • stringByAppendingPathComponent:suggestedFileName:
      • 执行文件剪切操作[NSFileManager defaultManager]moveItemAtURL:toURL:[NSURL fileURLWithPath:]error:
    • 当前方法存在问题:没有办法监听文件下载进度

    • 直接使用block块下载文件

      • 优点:内部已经完成了边接收数据边写入到沙盒的操作,解决了内存飙升的问题
      • 缺点:无法监听文件的下载进度
    设置代理来下载文件
    • 确定请求路径

    • 创建请求对象

    • 创建会话对象

      • sessionWithConfiguration:delegate:delegateQueue:
    • 创建请求任务NSURLSessionDownloadTask

      • downloadTaskWithRequest:
    • 执行任务resume

    • 遵守代理协议<NSURLSessionDownloadDelegate>

    • 实现代理方法

      • didWriteData:totalBytesWritten:totalBytesExpectedToWrite:写数据的时候调用该方法
        • 参数一:本次写入数据的大小
        • 参数二:写入数据的总大小
        • 参数三:文件的总大小
        • 计算文件下载进度
      • disFinishDownloadingToURL:当下载完成之后会调用该方法
        • 参数location:文件的临时存储路径
        • 剪切文件到caches路径
          • 通过downloadTask可以得到响应头信息
      • didCompleteWithError:请求结束的时候调用
    • 设置代理下载文件

      • 可以监听文件的下载进度
    NSURLSessionDownloadTask常见操作
    • 定义一个NSURLSessionDownloadTask属性,懒加载
    • 在懒加载方法里面发送请求,设置代理
    • 遵守协议
    • 实现代理方法
    • 开始start
      • resume
    • 暂停suspend
      • suspend
    • 继续resume
      • resume
    • 取消cancel
      • cancel 该取消方法是不能恢复的
    NSURLSessionDownloadTask断点下载
    • 应用场景:文件下载一半,点击了取消,点击恢复下载能够继续下载后面的文件
    • 定义一个NSData *resumeData;
    • 定义NSURLSession属性,懒加载
    • cancel方法里面
      • 调用cancelByProducingResumeData:^(NSData *_Nullable resumeData){}方法
      • 该取消方法是可以恢复的,resumeData是可恢复数据,不是文件本身,是记录信息,记录下载了哪个文件,下载到哪个位置
      • self.resumeData = resumeData;
    • resume方法里面
      • 重新创建网络请求 self.session downloadTaskWithResumeData:根据可恢复下载的数据,创建一个新的网络请求,发送请求

      • 上面的做法只考虑了点击了取消之后恢复的情况,需要进行判断是暂停之后恢复,还是取消之后恢复

        • 判断resumeData有没有值,再根据可恢复下载的数据重新创建网络请求
    离线断点下载(DownloadTask是没有办法实现离线断点下载的)
    • 思路:可以在程序退出之前,手动调用cancel方法,拿到可恢复下载数据,尝试保存到沙盒里面,当下次程序启动之后再尝试加载可恢复下载的数据,如果有该数据,那么就直接根据该数据创建一个新的网络请求

      • 注意:在程序退出之前,downloadTask就已经取消了,实际上不可行的,没有办法实现离线断点下载的
    • 总结:

      • 在处理文件下载的时候如果不需要实现离线断点下载功能,那么我们就使用NSURLSessionDownloadTask下载,否则就只能使用NSURLSessionDataTask

    文件上传

    文件上传分析

    • 文件上传参数
      • 文件参数(上传的文件是什么)
      • 用户名(谁上传了文件)
    • 处理逻辑(步骤)文件上传要发POST请求
      • 确定请求路径
      • 创建可变请求对象
      • 修改请求方法POST
      • 设置请求头:告诉服务器这是一个文件上传请求,请准备接收我的数据
      • 拼接参数-(设置请求体)
        • 按照固定的格式来拼接参数
      • 创建会话对象
      • 根据会话对象创建请求uploadTask请求
      • 发送请求
      • 解析服务器返回的数据
    文件上传格式简化:
    `分隔符`:----WebKitFormBoundaryjh7urS5p3OcvqXAT
    
    请求头
    Content-Type:multipart/form-data; boundary=分隔符
    
    拼接参数的格式
     文件参数
    /*
        --分隔符
        Content-Disposition: form-data; name="file"; filename="Snip20160716_103.png"
        Content-Type: image/png
        空行
        文件数据
     */
     非文件参数
    /*
        --分隔符
        Content-Disposition: form-data; name="username"
        空行
        xiaomage
    */
     结尾标识
    /*
    --分隔符--
    */
    
    

    文件上传实现

    • 先写思路再写代码
    #define Kboundary @"----WebKitFormBoundaryjh7urS5p3OcvqXAT
    "
    确定请求路径
    创建可变的请求对象
    修改请求方法为POST
    设置请求头
    //Content-Type:multipart/form-data; boundary=分隔符
    [NSString stringWithFormat:@"multipart/form-data;boundary = %@",Kboundary]
    [request setValue:forHTTPHeaderField:@"Content-Type"]
    拼接参数- 设置请求头
    NSData *data = 提供一个方法拼接参数
    //!!!!无效被忽略request.HTTPBody = data;
    创建会话对象
    根据会话对象创建uploadTask请求
    uploadTaskWithRequest:fromData:completionHandler:
         参数一:请求对象
         参数二:要传递的是本应该设置为请求体的参数
         参数三:当上传完成的时候调用
             data:响应体 
             response:响应头信息
    发送请求resume
    解析服务器返回的数据JSON数据解析
    
    按照固定形式拼接参数
    #define Kboundary  @"----WebKitFormBoundaryjh7urS5p3OcvqXAT"
    #define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
    -(NSData *)getBodyData
    {
        NSMutableData *data = [NSMutableData data];
        
        //01 文件参数
        /*
         --分隔符
         Content-Disposition: form-data; name="file"; filename="Snip20160716_103.png"
         Content-Type: image/png
         空行
         文件数据
         */
        
        [data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        //file 文件参数 参数名 == username
        //filename 文件上传到服务器之后以什么名称来保存
        [data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"123.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        
        //Content-Type 文件的数据类型
        //file和username是参数
        //分隔符可以随意些,但是不能有中文
        [data appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        [data appendData:KNewLine];
        NSData *imageData = [NSData dataWithContentsOfFile:@"/Users/xiaomage/Desktop/Snip20160716_125.png"];
        [data appendData:imageData];
        [data appendData:KNewLine];
        
        //02 非文件参数
        /*
         --分隔符
         Content-Disposition: form-data; name="username"
         空行
         xiaomage
         */
        [data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        //username 参数名称
        [data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        [data appendData:KNewLine];
        [data appendData:[@"xiaomage" dataUsingEncoding:NSUTF8StringEncoding]];
        [data appendData:KNewLine];
        
        //03 结尾标识
        /*
         --分隔符--
         */
        [data appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
        
        //拼接
        return data;
    }
    

    监听文件上传进度

    • 代理方法
      • 创建会话对象
        • sessionWithConfiguration:delegate:delegateQueue:
      • 遵守协议
      • 实现代理方法didSendBodyData:...
        • 参数一:本次上传的数据大小
        • 参数二:已经上传数据的总大小
        • 参数三:文件的总大小

    获得文件的MIMEType

    • Content-Type 又叫做MIMEType
    • 怎么获得MIMEType?
      • 发送请求,得到相应头信息(MIMEType)
      • response.MIMEType
      • 百度MIMEType,查表
      • 使用C语言的函数来获取
        • 导入MobileCoreServices
      • 设置为通用的二进制数据类型
        • application/octet-stream
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
    
        //01 发送请求|得到响应头信息(MIMEType)
        //02 直接百度查表 http://www.w3school.com.cn/media/media_mimeref.asp
        //03 使用C语言的函数来获取
        //04 设置为通用的二进制数据类型 application/octet-stream
       NSLog(@"%@",[self mimeTypeForFileAtPath:@"/Users/xiaomage/Desktop/Snip20160716_103.png"]);
    }
    
    
    - (NSString *)mimeTypeForFileAtPath:(NSString *)path
    {
        if (![[[NSFileManager alloc] init] fileExistsAtPath:path]) {
            return nil;
        }
        
        CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);
        CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
        CFRelease(UTI);
        if (!MIMEType) {
            return @"application/octet-stream";
        }
        return (__bridge NSString *)(MIMEType);
    }
    
    
    -(void)getMimetype
    {
       
        
        [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL fileURLWithPath:@"/Users/xiaomage/Desktop/上课笔记.h"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            //MIMEType = 大类型/小类型
            NSLog(@"%@",response.MIMEType);
        }] resume];
    }
    
    

    文件的压缩解压缩

    • 第三方框架ZipArchive
      • SSZipArchive
      • 报错:需要依赖于某个库,这个库不存在
        • 尝试添加这个库General/BuildPhrase
        • libz.1.1.3
    • 压缩
      • 导入SSZipArchive的头文件
      • 桌面上有两张图片,把这两张图片打包成zip文件
      • 压缩文件createZipFileAtPath:withFilesAtPath:
        • 参数一:打包后文件存储位置
        • 参数二:数组,要压缩的是哪些文件(要压缩的所有文件的文件路径)
        • 注意:开发中,文件是在沙盒里
      • 压缩文件夹creatZipFileAtPath:withContentsOfDirectory:
        • 参数一:打包后文件存储位置
        • 参数二:文件夹的路径
    • 解压缩
      • unzipFileAtPath:toDestination:
        • 参数一:要解压的文件路径
        • 参数二:解压后存储的路径

    多值参数

    • 确定请求路径
    • 创建会话对象
      • sharedSession
    • 创建请求任务
      • dataTaskWithURL:
    • 执行任务
    • 解析数据
      • JSON->OC
      • NSJSONSerilization JSONObjectWithData:
      • 中文输出处理
        • 分类:Foundation+Log
        • 控制字典和数组的输出,分类里面重写了系统的方法,决定字典和数组输出的内容
        • 给字典写的分类
          • 创建可变字符串
          • {遍历整个字典,每遍历一次就拼接key、value}
        • 给数组写的分类
          • 创建可变字符串
          • [遍历数组,每遍历一次就把里面的元素变成字符串拼接一次]

    NSURLSession配置

    • 在一个控制器里面发多个网络请求

      • 定义NSURLSession属性,懒加载
      • 确定请求路径
      • 创建可变的请求对象
      • 多个网络请求,怎么控制请求的超时时间
        • 配置信息
      • 创建会话对象
      • 发送请求,执行任务
    • 配置信息,控制多个请求的超时时间

      • 懒加载方法里面sessionWithConfiguration:根据配置信息创建会话对象
      • 拿到配置信息,对会话对象进行配置,控制控制器里的所有网络请求
      • configuration.timeoutIntervalForRequest = 10;所有网络请求都会遵守这个配置
      • 设置在蜂窝网络的状态系是否能够发生请求(比如视频播放,检测当前用户的网络情况)
    • 详细笔记:NSURLConfiguration

      • 目的是想取代request
      • defaultSessionConfiguration标准的配置
      • ephmeralSessionConfiguration返回的是预设的配置,没有缓存,无痕浏览
      • backgroundSessionConfiguration 后台,支持后台下载后台上传,会创建一个后台会话,可以在应用程序崩溃,挂起的状态下进行上传下载任务
    • 重要属性

      • HTTPAdditionalHeaders
      • allowCellularAccess 允许蜂窝网络
      • timeoutIntervalForRequest 请求超时时间

    总结

    • NSURLSession

      • GET|POST|设置代理
      • POST请求步骤
        • 创建请求路径
        • 创建可变的请求对象
        • 修改请求方法为POST
        • 设置参数
        • 创建会话对象
        • 创建请求任务
        • 发送请求
        • 解析服务器返回的数据
    • 数据解析

      • JSON解析
        • 反序列化处理 JSON->OC
        • 序列化处理 OC->JSON
        • 保存假数据加载假数据
      • XML解析
        • 语法
        • 解析XML
          • DOM:把整个XML文档加载进内存解析 GDataXML
          • SAX:从根元素开始,一个元素一个元素的开始解析 NSXMLParser
    • 文件下载

      • 使用dataWithContentURL:存在一些问题
        • 只能处理小文件,如果文件过大会造成内存飙升
        • 不能监听下载进程
        • 性能不好,contentsOfURL:是在当前线程中执行的
      • NSURLSession(DataTask-代理)
        • 监听文件的下载进度
        • 解决内存飙升问题

    相关文章

      网友评论

        本文标题:NSURLSession

        本文链接:https://www.haomeiwen.com/subject/jeqgyttx.html