最近在做关于文件下载的需求:前端调用接口,然后对返回数据进行过滤、格式化,然后按表格内容拼接生成csv文件,让用户下载。
多值参数
- 确定请求路径
- URLWithString:@"http://192.168.13.1:80/weather?place=Guangzhou&place=Beijing"
- 创建会话对象
- sharedSession
- 创建请求任务
- dataTaskWithURL:
- 执行任务
- 解析数据
- JSON->OC
- NSJSONSerilization JSONObjectWithData:
- 中文输出处理
- 分类:Foundation+Log
- 控制字典和数组的输出,分类里面重写了系统的方法,决定字典和数组输出的内容
- 给字典写的分类
- 创建可变字符串
- {遍历整个字典,每遍历一次就拼接key、value}
- 给数组写的分类
- 创建可变字符串
- [遍历数组,每遍历一次就把里面的元素变成字符串拼接一次]
具体实现方式如下:
文件的压缩解压缩
- 第三方框架ZipArchive
- SSZipArchive
- 报错:需要依赖于某个库,这个库不存在
- 尝试添加这个库General/BuildPhrase
- libz.1.1.3
- 压缩
- 导入SSZipArchive的头文件
- 桌面上有两张图片,把这两张图片打包成zip文件
- 压缩文件createZipFileAtPath:withFilesAtPath:
- 参数一:打包后文件存储位置
- 参数二:数组,要压缩的是哪些文件(要压缩的所有文件的文件路径)
- 注意:开发中,文件是在沙盒里
- 压缩文件夹creatZipFileAtPath:withContentsOfDirectory:
- 参数一:打包后文件存储位置
- 参数二:文件夹的路径
- 解压缩
- unzipFileAtPath:toDestination:
- 参数一:要解压的文件路径
- 参数二:解压后存储的路径
- unzipFileAtPath:toDestination:
- 数据中存在 ‘,‘ 逗号问题处理:将整个数据用双引号(英文格式)包裹起来,这样会显示成一个单元格。
小文件下载
- 文件下载的第一种方式:NSData dataWithContentsOfURL:
- 局限性:
- 只能处理小文件,如果文件过大会造成内存飙升
- 不能监听下载进度
- 性能不好
- 一般开发中不用这种方法下载文件,以后尽量不要使用了
2. 拼接后的数据如何直接下载,不需要用户在操作:动态创建a标签,并调用点击事件
常用操作
- 定义一个请求任务属性,写一个懒加载
- 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
时间: 2019-11-20阅读: 246标签: 文件
监听文件上传进度
- 代理方法
- 创建会话对象
- sessionWithConfiguration:delegate:delegateQueue:
- 遵守协议
- 实现代理方法didSendBodyData:...
- 参数一:本次上传的数据大小
- 参数二:已经上传数据的总大小
- 参数三:文件的总大小
- 创建会话对象
- 中文乱码解决方案:在数据前面加上字符串"ufeff"即可
代理方法的使用
- 确定请求路径
- 创建请求对象
- 创建|获取会话对象
- 自定义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:
let sourceData = { head: [ ‘时间‘, ‘成交价格‘, ‘成交数量‘, ‘手续费‘, ‘成交金额‘, ], data: [ {time: ‘2019-10-17 14:54:52‘, tradePrice: ‘30.0022.001.32 TWD‘, fee:‘0 TWD‘, tradeAmount: ‘660.00‘,}, {time: ‘2019-10-17 14:54:36‘, tradePrice: ‘30.0089.005.34 TWD‘, fee:‘0 TWD‘, tradeAmount: ‘2,670.00‘,}, {time: ‘2019-10-17 14:54:07‘, tradePrice: ‘21.00500.0021 TWD‘, fee:‘0 TWD‘, tradeAmount: ‘10,500.00‘,}, ]}// 格式化const data = [sourceData.head.join(‘,‘)].concat(sourceData.data.map(item = { return [ item.time, `"${item.tradePrice}"`, `"${item.fee}"`, `"${item.tradeAmount}"`, ].join(‘,‘)}))// 创建Blob对象 传入一个合适的MIME类型const blob = new Blob([‘ufeff‘ + data.join(‘n‘)], {type: ‘text/csv,charset=UTF-8‘}); // 参考链接 -CN/docs/Web/API/Blob// 使用 Blob 创建一个指向类型化数组的URLconst csvUrl = URL.createObjectURL(blob); // 参考链接 -CN/docs/Web/API/URL/createObjectURLlet link = document.createElement(‘a‘); link.download = `details_${new Date().getTime()}.csv`; //文件名字 link.href = csvUrl;// 触发下载link.click();
文件下载
注意事项:
解决内存飙升的问题- 输出流的基本使用
- 解决内存飙升问题:
- 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:
- 好处:不需要创建空的文件,也不需要判断
JSON反序列化处理(JSON->OC)
- 确定请求路径NSURL
- 创建请求对象NSURLRequest
- 创建会话对象NSURLSession
- 10bet,创建task
- dataTaskWithRequest:completionHander:
- 执行任务resume
- 解析数据
- 解析JSON数据(data本质上是一个JSON数据)
- NSJSONSerialization JSONObjectWithData:options:error:
- 参数一:要解析的JSON数据
- 参数二:选项,枚举[3]
- MutableContainers生成的字典或者数组是可变的
- MutableLeaves 生成的字典中的(key)字符串也是可变的
- AllowFragments 最外层既不是字典也不是数组
- 默认做法是传0,kNilOptions
- 参数三:错误信息,传地址
- 返回值:是一个对象
- 把一个JSON字符串转成OC的数据类型(字典|数组|其他)
XML(不需要掌握)
- 学习网站:W3school.com
- XML文档组成:
- 文档声明:<?xml version = "1.0"?>
- 元素Element
- 开始标签
- 结束标签
- 拥有内容的元素:<video>小黄人</video>
- 没有内容的元素:<video></video>
- 没有内容的元素简写:<video/>
- 一个元素可以嵌套若干个子元素(不能出现交叉嵌套)
- 注意:所有的空格和换行,都会当做具体内容处理
- 属性Attribute
- 一个元素里面可以拥有多个属性
- <video name = "小黄人 " length = "30" />
- video元素拥有name和length两个属性
- 属性值必须用“”或者‘’括住
- 实际上,属性表示的信息也可以用子元素来表示
监听文件下载进度
- 下载进度 = 当前已经下载的数据大小/文件的总大小
- 如何获得当前已经下载的数据大小?
- 第一种方法:self.fileData.length
- 第二种方法:不断累加data的大小
- 如何获得文件的总大小?
- 通过响应头来得到
- 在didReceiveResponse:方法里面拿响应头
- 得到本次请求的文件数据大小response.expectedContentLength;
- 在didReceiveData:方法里面计算进度
- 1.0*self.fileData.length/self.tatalSize
- 问题:内存飙升的问题!!
- 原因:fileData属性变量占用内存空间
- 解决:把拿到的每一块数据直接写入到沙盒(边接受数据边把数据写入到沙盒)
设置代理来下载文件
确定请求路径
创建请求对象
创建会话对象
- sessionWithConfiguration:delegate:delegateQueue:
创建请求任务NSURLSessionDownloadTask
- downloadTaskWithRequest:
执行任务resume
遵守代理协议<NSURLSessionDownloadDelegate>
实现代理方法
- didWriteData:totalBytesWritten:totalBytesExpectedToWrite:写数据的时候调用该方法
- 参数一:本次写入数据的大小
- 参数二:写入数据的总大小
- 参数三:文件的总大小
- 计算文件下载进度
- disFinishDownloadingToURL:当下载完成之后会调用该方法
- 参数location:文件的临时存储路径
- 剪切文件到caches路径
- 通过downloadTask可以得到响应头信息
- didCompleteWithError:请求结束的时候调用
- didWriteData:totalBytesWritten:totalBytesExpectedToWrite:写数据的时候调用该方法
设置代理下载文件
- 可以监听文件的下载进度
简介
- 使用步骤
- 使用NSURLSession会话对象创建Task,然后执行Task
- Task类型
- NSURLSessionTask
- NSURLSessionDataTask(GET/POST)
- NSURLSessionUploadTask(文件上传)
- NSURLSessionDownloadTask(文件下载)
- NSURLSessionDataTask(GET/POST)
- NSURLSessionTask
- 获得NSURLSession
- 方法一:获得共享的Session
- sharedSession
- 方法二:自定义Session
- sessionWithConfiguration:delegate:delegateQueue:
- 方法一:获得共享的Session
- 创建NSURLSessionTask
离线断点下载(DownloadTask是没有办法实现离线断点下载的)
思路:可以在程序退出之前,手动调用cancel方法,拿到可恢复下载数据,尝试保存到沙盒里面,当下次程序启动之后再尝试加载可恢复下载的数据,如果有该数据,那么就直接根据该数据创建一个新的网络请求
- 注意:在程序退出之前,downloadTask就已经取消了,实际上不可行的,没有办法实现离线断点下载的
总结:
- 在处理文件下载的时候如果不需要实现离线断点下载功能,那么我们就使用NSURLSessionDownloadTask下载,否则就只能使用NSURLSessionDataTask
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:
数据解析
文件断点下载
- 应用场景
- 当用户点击了取消之后想要继续下载文件(默认:从头开始把整个文件下载--缺点:浪费流量)
- 实现条件
- 知道应该下载哪一部分数据 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]
JSON和XML比较
- 同一份数据,既可以用JSON来表示,也可以用XML来表示
- JSON更加轻量级
- JSON的体积小于XML,所以服务器返回给移动端的数据以JSON居多
NSURLSessionDataTask下载文件
直接使用block下载文件
确定请求路径
创建请求对象
创建会话对象
根据会话对象创建一个downloadTask
- NSURLSessionDownloadTask--6个方法
- downloadTaskWithRequest:completionHandler:
- 参数一:请求对象
- 参数二:完成之后的回调
- location:文件的临时存储路径,该方法内部会自动完成边接收数据边写沙盒的操作
- response:响应头信息
- error:错误信息
发送网络请求resume
当文件下载完毕之后,剪切文件到安全的位置(从临时路径转移到cache路径)
- NSSearchPathForDirectoriesInDomains()
- stringByAppendingPathComponent:suggestedFileName:
- 执行文件剪切操作[NSFileManager defaultManager]moveItemAtURL:toURL:[NSURL fileURLWithPath:]error:
当前方法存在问题:没有办法监听文件下载进度
直接使用block块下载文件
- 优点:内部已经完成了边接收数据边写入到沙盒的操作,解决了内存飙升的问题
- 缺点:无法监听文件的下载进度
获得文件的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];
}
复杂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
文件上传实现
- 先写思路再写代码
#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 [@"rn" 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;
}
本文由10bet发布于Web前端,转载请注明出处:NSURLSession
关键词: