NSURLSession基本使用
简介
- 使用步骤
- 使用NSURLSession会话对象创建Task,然后执行Task
- Task类型
- NSURLSessionTask
- NSURLSessionDataTask(GET/POST)
- NSURLSessionUploadTask(文件上传)
- NSURLSessionDownloadTask(文件下载)
- NSURLSessionDataTask(GET/POST)
- NSURLSessionTask
- 获得NSURLSession
- 方法一:获得共享的Session
- sharedSession
- 方法二:自定义Session
- sessionWithConfiguration:delegate:delegateQueue:
- 方法一:获得共享的Session
- 创建NSURLSessionTask
如何发送GET请求
方法一
- 01.确定请求路径NSURL
- 02.创建请求对象NSURLRequest
- 03.创建|获取会话对象
- sharedSession
- 自定义session
- 04.根据会话对象来创建请求任务
- dataTaskWithRequest:
- 参数一:请求对象
- 参数二:completionHandler完成之后调用的代码块
- data:响应体
- response:响应头
- error:错误信息
- dataTaskWithRequest:
- 05.执行任务(发送请求)
- [dataTask resume];
- resume:NSURLSession创建好之后默认是挂起状态需要调用resume恢复
- [dataTask 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:
- 解析数据
- 当接收到服务器响应的时候调用didReceiveResponse:completionHander:
登录小案例
- 用户名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
- 告诉编译器在编译的时候以非ARC进行编译
- 解析数据
- 加载整个XML文档
- GDataDocument alloc initWithData:options:error:
- 得到根元素,根据根元素得到内部所有名称为video的子元素
- [.rootElement elementsForName:]返回值是数组
- 遍历所有的子元素,得到子元素中的属性
- for in
- 元素的类型:GDataXMLElement
- 创建一个空的模型
- 给模型里的属性赋值
- video.ID = [element attributeForName:@"id"].stringValue;
- addObject:把模型添加到可变数组中
- 加载整个XML文档
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:
- 获得caches路径
- 处理拼接得到的数据(保存到沙盒里面)
- 接收到服务器响应的时候调用didReceiveResponse:completionHandler:
- 创建请求任务
- 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:
- 参数一:要创建的文件保存的位置
- 参数二:文件内容
- 参数三:文件属性
- 在didReceiveResponse:方法里
- 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]
- 创建输出流NSOutputStream alloc initToFileAtPath:append:
- 好处:不需要创建空的文件,也不需要判断
常用操作
- 定义一个请求任务属性,写一个懒加载
- NSURLSessionDataTask 属性用strong修饰
- 在cancel方法之后手动清空
- NSURLSessionDataTask 属性用strong修饰
- 开始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再创建空的文件,否则不再创建
- 重新发送网络请求,重新走了didRecevieResponse,又创建了一个空的文件,但是第一次已经创建了一个空的文件,怎么判断之前有文件了?
-
bug:点击了取消之后,调用didCompleteError:handler被释放掉了,下载后面的数据的时候,重新创建了句柄指针,指向文件的开头,边写边移动,直接覆盖,怎么让句柄指针,让它指向文件的末尾,再让它写数据!
- 让文件句柄指向文件的末尾[self.handle seekToEndOfFile]
离线断点下载优化(进度条)
当程序重新启动后显示实时的进度信息!!
怎么做到实时显示数据?
-
当计算文件总大小之后,把文件数据的总大小就保存到沙盒里去
- 把文件的总大小包装成NSData
- stringWithFormat:先转换成字符串
- dataUsingEncoding:把字符串转为NSData
- 把文件的总大小包装成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方法里面调用
- 代理的问题:给NSURLSession设置代理,控制器没有办法释放
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:请求结束的时候调用
- didWriteData:totalBytesWritten:totalBytesExpectedToWrite:写数据的时候调用该方法
-
设置代理下载文件
- 可以监听文件的下载进度
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:
- 参数一:要解压的文件路径
- 参数二:解压后存储的路径
- unzipFileAtPath:toDestination:
多值参数
- 确定请求路径
- URLWithString:@"http://192.168.13.1:80/weather?place=Guangzhou&place=Beijing"
- 创建会话对象
- 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
- JSON解析
-
文件下载
- 使用dataWithContentURL:存在一些问题
- 只能处理小文件,如果文件过大会造成内存飙升
- 不能监听下载进程
- 性能不好,contentsOfURL:是在当前线程中执行的
- NSURLSession(DataTask-代理)
- 监听文件的下载进度
- 解决内存飙升问题
- 使用dataWithContentURL:存在一些问题