基本概念
- 在网络编程中,有几个必须掌握的基本概念:
客户端(Client)
:移动应用(iOS、android等应用);
服务器(Server)
:为客户端提供服务、提供数据、提供资源的机器;
请求(Request)
:客户端向服务器索取数据的一种行为;
响应(Response)
:服务器对客户端的请求做出的反应,一般指返回数据给客户端。
- 作为移动开发工程师,主要的精力都是放在
客户端
开发。
服务器
- 按照软件开发阶段来分,服务器可以大致分为2种:
1、远程服务器
别名
:外网
服务器、正式
服务器;
使用阶段
:应用上线
后使用的服务器;
使用人群
:供全体用户使用;
速度
:取决于服务器的性能、用户的网速。
2、本地服务器
别名
:内网
服务器、测试
服务器;
使用阶段
:应用处于开发、测试
阶段使用的服务器;
使用人群
:仅供公司内部的开发人员、测试人员使用;
速度
:由于是局域网
,所以速度飞快,有助于提高开发测试效率。
HTTP
- 网络中部署着各种各样的服务器,比如腾讯的服务器、百度的服务器
- 那么问题来了?。
-
客户端如何找到想要连接的服务器
。 -
客户端通过URL找到想要连接的服务器
。
- URL
a、
URL
的全称是Uniform Resource Locator(统一资源定位符)
。
b、通过1个URL,能找到互联网上唯一
的1个资源。
c、URL
就是资源的地址、位置
,互联网上的每个资源都有一个唯一
的URL
。
- URL的基本格式 =
协议://主机地址/路径
。
协议
:不同的协议,代表着不同的资源查找方式、资源传输方式;
主机地址
:存放资源的主机(服务器)的IP地址(域名);
路径
:资源在主机(服务器)中的具体位置。
- URL中常见的协议:
HTTP
a、超文本传输协议,访问的是远程的网络资源,格式是http://;
b、http协议是在网络开发中最常用的协议
。
file
a、访问的是本地计算机上的资源,格式是file://(不用加主机地址)
mailto
a、访问的是电子邮件地址,格式是mailto:
FTP
a、访问的是共享主机的文件资源,格式是ftp://
TCP/IP协议簇
为了能够理解
HTTP
,我们需要先了解TCP/IP
协议簇。通常意义上,我们使用的网络是在TCP/IP协议簇
的基础上运作的,而HTTP
属于它内部的一个子集。
计算机与网络设备需要通信,双方就必须要基于相同的方法,比如具体应该如何探测通信目标,由哪一方面发起通信,使用什么语言进行沟通等等,所有的这一切都需要规则。而我们则把这些
规则称之为协议(potocol)
。
在协议中规定了很多的各式各样的内容,如选址方法,双方建立通信的顺序等等。这些协议如
(ICMP DNS TCP FTP HTTP SNMP PPPoE IP FDDI)
等等,通常我们把TCP/IP
认为是在IP协议
的通信过程中,使用到的协议簇
的统称。
TCP 协议簇
里面最重要的一点就是分层设计
:按照层次分别分为应用层、传输层、网络层和数据链路层
。其中,与HTTP
关系密切的协议有TCP、IP、DNS
等。
-
TCP/IP参考模型
HTTP协议简介
- 不管是
移动客户端
还是PC端
,访问远程的网络资源经常使用HTTP协议
。
@ 访问小码哥主页:http://www.baidu.com;
@ 获得网易的新闻数据;
@ 获得优酷的视频数据。
思考?
1.客户端该传什么格式的数据给服务器?服务器才能看懂
2.服务器该返回什么格式的数据给客户端?客户端才能看懂
3.两边要怎样传输数据才能有效沟通?
- HTTP协议的作用
1.
HTTP
的全称是Hypertext Transfer Protocol,超文本传输协议
。
2.规定客户端
和服务器
之间的数据传输格式。
3.让客户端
和服务器
能有效地进行数据沟通。
- HTTP协议的特点(为什么选择HTTP)
简单快速:
因为HTTP协议简单,所以HTTP服务器的程序规模小,因而通信速度很快
灵活:
HTTP允许传输各种各样的数据
HTTP 0.9和1.0使用非持续连接:
限制每次连接只处理一个请求,服务器对客户端的请求做出响应后,马上断开连接,这种方式可以节省传输时间
- HTTP的基本通信过程
要想使用
HTTP协议
向服务器
索取数据,得先了解HTTP的通信过程
。
完整的http通信
可以分为2
大步骤:
请求
:客户端
向服务器
索要数据。
响应
:服务器
返回客户端
相应的数据。
- 发送HTTP请求的方法
@ 在HTTP/1.1协议中,定义了8种发送http请求的方法
@GET
、POST
、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
@ 根据HTTP协议的设计初衷,不同的方法对资源有不同的操作方式
@ PUT :增
@ DELETE :删
@POST
:改
@GET
:查
@ 最常用的是GET
和POST
(实际上GET
和POST
都能办到增删改查
)。
- 要想使用
GET
和POST
请求跟服务器进行交互,得先了解一个概念
参数
就是传递给服务器的具体数据,比如登录时的帐号、密码
-
GET
和POST
对比:GET
和POST
的主要区别
表现在数据传递
上
GET
a.在请求URL后面以?
的形式跟上发给服务器的参数,多个参数之间用&
隔开,比如http://ww.test.com/login?
username=123&
pwd=234&
type=JSON
b.由于浏览器和服务器对URL长度有限制,因此在URL后面附带的参数是有限制的,通常不能超过1KB
POST
a.发给服务器的参数全部放在请求体
中。
a.理论上,POST
传递的数据量没有限制(具体还得看服务器的处理能力)
-
GE
T和POST
的选择建议
a、如果要传递大量数据,比如
文件上传
,只能用POST
请求。
b、GET
的安全性
比POST
要差
些,如果包含机密\敏感信息
,建议用POST
。
c、如果仅仅是索取数据(数据查询)
,建议使用GET
。
d、如果是增加、修改、删除
数据,建议使用POST
。
- HTTP报文
- HTTP报文细节(请求)
- HTTP报文细节(响应)
HTTP通信过程 - 请求
- HTTP协议规定:1个完整的由
客户端
发给服务器
的HTTP请求中包含以下内容:
请求头
:包含了对客户端的环境描述、客户端请求信息
等。- GET /minion.png HTTP/1.1 // 包含了请求方法、请求资源路径、HTTP协议版本
- Host: 120.25.226.186:32812 // 客户端想访问的服务器主机地址
- User-Agent: Mozilla/5.0 // 客户端的类型,客户端的软件环境
- Accept: text/html, / // 客户端所能接收的数据类型
- Accept-Language: zh-cn // 客户端的语言环境
- Accept-Encoding: gzip // 客户端支持的数据压缩格式
请求体
:客户端
发给服务器
的具体数据,比如文件数据(POST
请求才会有).
HTTP通信过程 - 响应
-
客户端
向服务器
发送请求,服务器
应当做出响应,即返回数据给客户端
。 -
HTTP
协议规定:1
个完整的HTTP响应中包含以下内容:
响应头
:包含了对服务器的描述、对返回数据的描述
。- HTTP/1.1 200 OK // 包含了HTTP协议版本、状态码、状态英文名称
- Server: Apache-Coyote/1.1 // 服务器的类型
- Content-Type: image/jpeg // 返回数据的类型
- Content-Length: 56811 // 返回数据的长度
- Date: Mon, 23 Jun 2014 12:54:52 GMT // 响应的时间
响应体
:服务器
返回给客户端
的具体数据,比如文件数据
。
- 常见响应状态码
状态码 | 英文名称 | 中文描述 |
---|---|---|
200 |
OK |
请求成功 |
400 |
Bad Request |
客户端请求的语法错误,服务器无法 |
404 |
Not Found |
服务器无法根据客户端的请求找到资源 |
500 |
Internal Server Error |
服务器内部错误,无法完成请求 |
iOS中发送HTTP请求的方案
- 在iOS中,常见的发送HTTP请求的方案有
苹果原生(自带)
NSURLConnection
:用法简单,最古老最经典最直接的一种方案【坑比较多】。NSURLSession
:功能比NSURLConnection
更加强大,苹果目前比较推荐使用这种技术【2013推出,iOS7开始出的技术】。CFNetwork
:NSURL*
的底层,纯C
语言。
第三方框架
ASIHttpRequest
:外号“HTTP终结者”
,功能极其强大,可惜早已停止更新。AFNetworking
:简单易用,提供了基本够用的常用功能,维护和使用者多。MKNetworkKit
:简单易用,产自三哥的故乡印度,维护和使用者少。
- 建议:
为了提高开发效率,企业开发用的基本是第三方框架
网络�NSURLConnection
- 常用类
-
NSURL
:请求地址 -
NSURLRequest
:一个NSURLRequest对象
就代表一个请求
,它包含的信息有:
1.
一个NSURL对象
2.请求方法、请求头、请求体
3.请求超时
… …
-
NSURLConnection
的使用步骤:
- 使用
NSURLConnection
发送请求的步骤很简单:
1.创建一个NSURL
对象,设置请求路径。
2.传入NSURL
创建一个NSURLRequest
对象,设置请求头
和请求体
。
3.使用NSURLConnection
发送请求。
- NSURLConnection发送请求
-
NSURLConnection
常见的发送请求方法有以下几种: 同步请求:
+(NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
-
异步请求:
根据对服务器返回数据的处理方式的不同,又可以分为2种
block
回调
+(void)sendAsynchronousRequest:(NSURLRequest*) request queue:(NSOperationQueue*) queue completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler;
2.
代理
-(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;
+(NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
-(id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately;
在
startImmediately = NO
的情况下,需要调用start
方法开始发送请求
-(void)start;
成为
NSURLConnection
的代理,最好遵NSURLConnectionDataDelegate
协议。
- NSURLConnectionDelegate
-
NSURLConnectionDataDelegate
协议中的代理方法
1.开始接收到服务器的响应时调用
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
2.接收到服务器返回的数据时调用
(服务器返回的数据比较大时会调用多次)
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
3.服务器返回的数据完全接收完毕后调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection;
4.请求出错时调用(比如请求超时)
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- NSURLConnection取消发送网络请求
-(void)canale;
NSURLConnection发送请求(GET)
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
/**
* <#注释#>
*/
@property (strong, nonatomic)NSMutableData *data;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//发送同步网络请求
// [self sendSync];
//发送异步网络请求1(block)
// [self sendAsync1];
//发送异步网络请求2(代理)
[self sendAsyncDelegate];
}
//发送异步网络请求2(代理方式回调)
- (void)sendAsyncDelegate{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//设置代理的第一种方式:自动发送网络请求
// NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
/*
设置代理的第二种方式:
第一个参数:请求对象
第二个参数:谁成为NSURLConnetion对象的代理
第三个参数:是否马上发送网络请求,如果该值为YES则立刻发送,如果为NO则不会发送网路请求
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//调用该方法控制网络请求的发送
[conn start];
*/
//设置代理的第三种方式:使用类方法设置代理,会自动发送网络请求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消网络请求
//[conn cancel];
}
/*
1.当接收到服务器响应的时候调用
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数response:接收到的服务器返回的响应头信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response{
}
/*
2.当接收到数据的时候调用,该方法会被调用多次
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数data:本次接收到的服务端返回的二进制数据(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{
[self.data appendData:data];
}
/*
3.当服务端返回的数据接收完毕之后会调用
通常在该方法中解析服务器返回的数据
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection{
NSString *string = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
NSLog(@"%@",string);
}
/*4.当请求错误的时候调用(比如请求超时)
第一个参数connection:NSURLConnection对象
第二个参数:网络请求的错误信息,如果请求失败,则error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error{
}
//发送异步网络请求1(blcok方式回调)
- (void)sendAsync1{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSURLRequest *request = [NSURLRequest requestWithURL:url];
/**
* 参数说明
*
* 第一个参数:NSURLRequest 请求对象
* 第二个参数:NSOperationQueue 队列
* 队列:(主队列 、自己创建)- 线程:决定了某个代码块在哪个线程中执行
* 主队列:在主线程中执行
* 自定义的队列:在子线程中执行
* 第三个参数:completionHandler 完成(请求成功|请求失败)后的回调
* response:响应头
* data:响应体信息
* connectionError:错误信息
*/
//03 NSURLConnection发送异步网络请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//05 解析服务器返回的数据data
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"result - %@",result);
}];
}
//发送同步网络请求
- (void)sendSync{
//01 确定请求路径(GET: 基础路径+参数)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 使用NSURLConnection发送同步请求(阻塞式)
/**
* 参数说明
*
* 第一个参数:NSURLRequest 请求对象
* 第二个参数:NSURLResponse 响应头信息
* 第三个参数:NSError 错误信息
*/
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//返回值data(响应体信息)服务器返回的数据
//打印查看响应头信息
NSLog(@"response - %@",response);
//04解析服务器返回的数据 (二进制数据转换成字符串)
/**
* 参数说明
*
* 第一个参数:要转换的二进制数据
* 第二个参数:编码规则
*/
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",result);
}
@end
NSURLConnection发送请求(POST)
#import "ViewController.h"
@interface ViewController ()
/**
* <#注释#>
*/
@property (strong, nonatomic)NSMutableData *data;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//发送同步POST网络请求
// [self sendSync];
//发送异步POST网络请求
// [self sendAsync];
//发送异步网络请求2(代理)
[self sendAsync2];
}
//发送异步网络请求2(代理方式回调)
- (void)sendAsync2{
//01 确定请求路径(GET: 基础路径+参数)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求对象请求方法(注意的是:必须是NSMutableURLRequest)
request.HTTPMethod = @"POST";
//设置超时时间15(超过15秒服务器就判定服务器请求失败,以error返回)
request.timeoutInterval = 15;
//设置请求头信息(key - value)
[request setValue:@"iOS 10.3" forHTTPHeaderField:@"User-Agent"];
//设置代理的第一种方式:自动发送网络请求
// NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
/*
设置代理的第二种方式:
第一个参数:请求对象
第二个参数:谁成为NSURLConnetion对象的代理
第三个参数:是否马上发送网络请求,如果该值为YES则立刻发送,如果为NO则不会发送网路请求
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//调用该方法控制网络请求的发送
[conn start];
*/
//设置代理的第三种方式:使用类方法设置代理,会自动发送网络请求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消网络请求
//[conn cancel];
}
/*
1.当接收到服务器响应的时候调用
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数response:接收到的服务器返回的响应头信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response{
}
/*
2.当接收到数据的时候调用,该方法会被调用多次
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数data:本次接收到的服务端返回的二进制数据(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data{
[self.data appendData:data];
}
/*
3.当服务端返回的数据接收完毕之后会调用
通常在该方法中解析服务器返回的数据
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection{
NSString *string = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
NSLog(@"%@",string);
}
/*4.当请求错误的时候调用(比如请求超时)
第一个参数connection:NSURLConnection对象
第二个参数:网络请求的错误信息,如果请求失败,则error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error{
}
//发送异步POST网络请求(block方式回调)
- (void)sendAsync{
//01 确定请求路径(GET: 基础路径+参数)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求对象请求方法(注意的是:必须是NSMutableURLRequest)
request.HTTPMethod = @"POST";
//设置超时时间15(超过15秒服务器就判定服务器请求失败,以error返回)
request.timeoutInterval = 15;
//设置请求头信息(key - value)
[request setValue:@"iOS 10.3" forHTTPHeaderField:@"User-Agent"];
//获取请求头所有的信息
// request.allHTTPHeaderFields;
//设置请求体(参数:)
/**
* 参数
* username:ooo
* pwd:999
* type:JSON
*/
//NSData 二进制数据
request.HTTPBody = [@"username=ooo&pwd=999&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
/**
* 参数说明
*
* 第一个参数:NSURLRequest 请求对象
* 第二个参数:NSOperationQueue 队列
* 队列:(主队列 、自己创建)- 线程:决定了某个代码块在哪个线程中执行
* 主队列:在主线程中执行
* 自定义的队列:在子线程中执行
* 第三个参数:completionHandler 完成(请求成功|请求失败)后的回调
* response:响应头
* data:响应体信息
* connectionError:错误信息
*/
//03 NSURLConnection发送异步网络请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//05 解析服务器返回的数据data
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"result - %@",result);
}];
}
//发送同步POST网络请求
- (void)sendSync{
//01 确定请求路径(GET: 基础路径+参数)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//02 创建请求对象
/**
* 该请求对象默认生成请求头信息 默认GET请求
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求对象请求方法(注意的是:必须是NSMutableURLRequest)
request.HTTPMethod = @"POST";
//设置超时时间15(超过15秒服务器就判定服务器请求失败,以error返回)
request.timeoutInterval = 15;
//设置请求头信息(key - value)
[request setValue:@"iOS 10.3" forHTTPHeaderField:@"User-Agent"];
//获取请求头所有的信息
// request.allHTTPHeaderFields;
//设置请求体(参数:)
/**
* 参数
* username:ooo
* pwd:999
* type:JSON
*/
//NSData 二进制数据
request.HTTPBody = [@"username=ooo&pwd=999&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//03 使用NSURLConnection发送同步请求(阻塞式)
/**
* 参数说明
*
* 第一个参数:NSURLRequest 请求对象
* 第二个参数:NSURLResponse 响应头信息
* 第三个参数:NSError 错误信息
*/
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//返回值data(响应体信息)服务器返回的数据
//打印查看响应头信息
NSLog(@"response - %@",response);
//04解析服务器返回的数据 (二进制数据转换成字符串)
/**
* 参数说明
*
* 第一个参数:要转换的二进制数据
* 第二个参数:编码规则
*/
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",result);
}
@end
- 创建GET和POST请求
- 创建GET请求
NSString *urlStr = [@"http://120.25.226.186:32812/login?username=123&pwd=123" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
- 创建POST请求
NSString *urlStr = @"http://120.25.226.186:32812/login";
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
//设置请求对象请求方法(注意的是:必须是NSMutableURLRequest)
request.HTTPMethod = @"POST";
//设置超时时间15(超过15秒服务器就判定服务器请求失败,以error返回)
request.timeoutInterval = 15;
//设置请求头信息(key - value)
[request setValue:@"iOS 10.3" forHTTPHeaderField:@"User-Agent"];
// 请求体
NSString *bodyStr = @"username=123&pwd=123";
request.HTTPBody = [bodyStr dataUsi
- URL的转码操作
只有GET请求时需要转码
NSString *urlStr =@"http://120.25.226.186:32812/login2?username=小码哥&pwd=520it&type=JSON";
//URL转码(处理URL有中文汉字的情况)
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",result);
网络NSURLSession
NSURLSession
- 使用步骤
使用NSURLSession
对象创建Task
,然后执行Task
Task
的类型
- 获得
NSURLSession
- 获得共享的
Session
+(NSURLSession *)sharedSession;
- 自定义
Session
+(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
NSURLSessionTask
常见方法:
-(void)suspend;
// 暂停
-(void)resume;
// 恢复
-(void)cancel;
// 取消
@property (readonly, copy) NSError *error;
// 错误
@property (readonly, copy) NSURLResponse *response;
// 响应
-
NSURLSession
发送GET
请求:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// [self get1];
// [self get2];
[self get3];
}
- (void)get3{
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString *resultString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",resultString);
}] resume];
}
- (void)get2{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//03 创建NSURLSession会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 会话对象(session)创建Task(任务)
/**
* 参数说明:
* url:请求路径
* dataTaskWithURL:该方法内部已经包装了一个NSURLRequest对象
* completionHandler:完成后的回调
* data:响应体信息
* response:响应头信息
* error:错误信息
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//06 返回请求路径
NSString *resultString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",resultString);
}];
//05 执行task;
[dataTask resume];
}
- (void)get1{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//02 创建NSURLRequest请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建NSURLSession会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 会话对象(session)创建Task(任务)
/**
* 参数说明:
* request:请求对象
* completionHandler:完成后的回调
* data:响应体信息
* response:响应头信息
* error:错误信息
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//06 返回请求路径
NSString *resultString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",resultString);
}];
//05 执行task
[dataTask resume];
}
@end
-
NSURLSession
发送POST
请求:
- (void)post{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//02 创建可变的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//02-1 设置请求方法
request.HTTPMethod = @"POST";
//02-2 设置请求体(传入的参数)
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//03 创建NSURLSession会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 会话对象(session)创建Task(任务)
/**
* 参数说明:
* request:请求对象
* completionHandler:完成后的回调
* data:响应体信息
* response:响应头信息
* error:错误信息
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//06 返回请求路径
NSString *resultString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",resultString);
}];
//05 执行task
[dataTask resume];
}
- 下面额
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {}
block块是在子线程
中执行,也是默认的。
[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {`
}];
-
NSURLSession
相关的代理方法 - 成为
NSURLSession
的代理,最好遵NSURLSessionDataDelegate
协议。
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 自定义NSURLSession会话对象(注意点:delegate、delegateQueue)
/**
*参数说明
*第一个参数:NSURLSessionConfiguration(配置信息:设置请求用的,功能类似于NSURLRequest<defaultSessionConfiguration>)
*第二个参数:delegate设置代理
*第三个参数:delegateQueue代理队列(线程)-> 决定了代理方法在哪个线程>中执行
*[NSOperationQueue mainQueue] -> 决定了代理方法在主线程中执行
*[[NSOperationQueue alloc] init] -> 决定了代理方法在子线程中执行
*如果传入nil -> 默认代理方法在子线程中执行
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
//会话对象(session)创建Task(请求任务)
/**
*参数说明:
*request:请求对象
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//05 执行task
[dataTask resume];
-
NSURLSessionDataDelegate
方法:
方法说明:
接受到服务器响应的时候调用
/**
*第一个参数:session:会话对象
*第二个参数:dataTask:请求任务
*第三个参数:response:响应头
*第四个参数:completionHandler:需要通过自己调用completionHandler(),告诉>系统应该如何处理服务器返回的数据
*/
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
/**
* 说明:需要通过自己调用completionHandler(),告诉系统应该如何处理服务器返回的数据
* 传入的参数:枚举值:NSURLSessionResponseDisposition
NSURLSessionResponseCancel = 0,
NSURLSessionResponseAllow = 1,
NSURLSessionResponseBecomeDownload = 2,
NSURLSessionResponseBecomeStream = 3
*/
completionHandler(NSURLSessionResponseAllow);
}
方法说明:
接受到服务器返回数据的时候调用(该方法会被调用多次)
/**
*第一个参数:session:会话对象
*第二个参数:dataTask:请求任务
*第三个参数:data:返回的数据
*/
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
}
方法说明:
请求完成或失败的时候调用(通过判断error是否有值来判断是否请求失败)
/**
*第一个参数:session:会话对象
*第二个参数:dataTask:请求任务
*第三个参数:error:错误信息
*/
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
}
-
NSURLSession代理方法
处理GET
请求:
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
/**
* 请求完成返回的数据(二进制)
*/
@property (strong, nonatomic)NSMutableData *resultData;
@end
@implementation ViewController
- (NSMutableData *)resultData{
if (!_resultData) {
_resultData = [NSMutableData data];
}
return _resultData;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//02 创建NSURLRequest请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 自定义NSURLSession会话对象
/**
* 参数说明
*
* 第一个参数:NSURLSessionConfiguration(配置信息:设置请求用的,功能类似于NSURLRequest<defaultSessionConfiguration>)
* 第二个参数:delegate设置代理
* 第三个参数:delegateQueue代理队列(线程)-> 决定了代理方法在哪个线程中执行
* [NSOperationQueue mainQueue] -> 决定了代理方法在主线程中执行
* [[NSOperationQueue alloc] init] -> 决定了代理方法在子线程中执行
* 如果传入nil -> 默认代理方法在子线程中执行
*
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
//04 会话对象(session)创建Task(请求任务)
/**
* 参数说明:
* request:请求对象
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//05 执行task
[dataTask resume];
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 方法说明:接受到服务器响应的时候调用
*
* 第一个参数:session:会话对象
* 第二个参数:dataTask:请求任务
* 第三个参数:response:响应头
* 第四个参数:completionHandler:需要通过自己调用completionHandler(),告诉系统应该如何处理服务器返回的数据
*
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@" --didReceiveResponse--");
/**
* 说明:需要通过自己调用completionHandler(),告诉系统应该如何处理服务器返回的数据
* 传入的参数:枚举值:NSURLSessionResponseDisposition
* NSURLSessionResponseCancel = 0,
NSURLSessionResponseAllow = 1,
NSURLSessionResponseBecomeDownload = 2,
NSURLSessionResponseBecomeStream API_AVAILABLE(macos(10.11), ios(9.0), watchos(2.0), tvos(9.0)) = 3
*/
completionHandler(NSURLSessionResponseAllow);
}
/**
* 方法说明:接受到服务器返回数据的时候调用(该方法会被调用多次)
*
* 第一个参数:session:会话对象
* 第二个参数:dataTask:请求任务
* 第三个参数:data:返回的数据
*
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
NSLog(@" --didReceiveData--");
[self.resultData appendData:data];
}
/**
* 方法说明:请求完成或失败的时候调用(通过判断error是否有值来判断是否请求失败)
*
* 第一个参数:session:会话对象
* 第二个参数:dataTask:请求任务
* 第三个参数:error:错误信息
*
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@" --didCompleteWithError--");
if (!error) {//请求成功
NSString *resultStr = [[NSString alloc] initWithData:self.resultData encoding:NSUTF8StringEncoding];
NSLog(@"success:%@",resultStr);
}else{//请求失败
NSLog(@"fail:%@",error);
}
}
@end
文件的下载
- 文件的下载
"普通下载"
:会造成内存飙升的现象
。
// ViewController.m
// 25-文件的下载
//
// Created by mac2016 on 2020/5/31.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
/**
* 文件数据
*/
@property (strong, nonatomic)NSMutableData *fileData;
/**
* 获的文件的总大小
*/
@property (assign, nonatomic)NSInteger totalSize;
/**
* 当前已经下载的数据大小
*/
@property (nonatomic, assign) NSInteger currentSize;
@end
@implementation ViewController
- (NSMutableData *)fileData{
if (!_fileData) {
_fileData = [[NSMutableData alloc] init];
}
return _fileData;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self downLoad_withBlock];
[self downLoad_withDelegate];
}
#pragma---------------------------------------
- (void)downLoad_withDelegate{
//01 确定请求路径
// NSURL *url = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"];
NSURL *url =[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建下载请求的task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//05 发送请求
[dataTask resume];
}
#pragma MARK - NSURLSessionDataDelegate
/**
* 01、方法说明:接收到响应的时候调用
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
//获取文件的总大小
self.totalSize = response.expectedContentLength;
}
/**
* 02、方法说明:接收到服务器返回数据的时候调用 可能会调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.fileData appendData:data];
//计算文件的下载进度并显示 = 已经下载的数据/文件的总大小
self.currentSize += data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
}
/**
* 03、方法说明:下载完成或者是失败的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {//下载成功
//得到文件的名称:得到请求的响应头信息,获取响应头信息中推荐的文件名称
//suggestedFilename == 就是URL的最后一个节点
NSString *fileName = [task.response suggestedFilename];
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
[self.fileData writeToFile:fullPath atomically:YES];
}else{//下载失败
NSLog(@"下载失败");
}
}
- (void)downLoad_withBlock{
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",data);
}] resume];
}
@end
-
文件句柄(指针)
下载文件:来解决内存飙升
文件句柄(指针):
NSFileHandle
特点:句柄在写数据的时候边写数据边移动位置
使用步骤:
1)创建空的文件
2)创建文件句柄指针指向该文件
3)当接收到数据的时候,使用句柄来写数据
4)当所有的数据写完,应该关闭句柄指针
// ViewController.m
// 25-文件的下载
//
// Created by mac2016 on 2020/5/31.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/**
* <#注释#>
*/
@property (strong, nonatomic)NSFileHandle *handle;
/**
* 获的文件的总大小
*/
@property (assign, nonatomic)NSInteger totalSize;
/**
* 当前已经下载的数据大小
*/
@property (nonatomic, assign) NSInteger currentSize;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"];
// NSURL *url =[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建下载请求的task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//05 发送请求
[dataTask resume];
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 01、方法说明:接收到响应的时候调用
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
//获取文件的总大小
self.totalSize = response.expectedContentLength;
//00 获取存储沙盒的路径
NSString *fileName = response.suggestedFilename;
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接文件的存储路径(沙盒路径cache) + 文件名
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
//01 创建空文件
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
//02 创建文件句柄
self.handle = [NSFileHandle fileHandleForWritingAtPath:fullPath];
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
/**
* 02、方法说明:接收到服务器返回数据的时候调用 可能会调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.handle writeData:data];
//计算文件的下载进度并显示 = 已经下载的数据/文件的总大小
self.currentSize += data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
}
/**
* 03、方法说明:下载完成或者是失败的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {//下载成功
[self.handle closeFile];
}else{//下载失败
NSLog(@"下载失败");
}
}
/**
文件句柄(指针):NSFileHandle(边接收数据,边写入到沙盒中)
特点:句柄在写数据的时候边写数据边移动位置
使用步骤:
1)创建空的文件
2)创建文件句柄指针指向该文件
3)当接收到数据的时候,使用句柄来写数据
4)当所有的数据写完,应该关闭句柄指针
*/
@end
- 文件
句柄指针
实现断点下载
// ViewController.m
// 25-文件的下载
//
// Created by mac2016 on 2020/5/31.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/**
* 获的文件的总大小
*/
@property (assign, nonatomic)NSInteger totalSize;
/**
* 当前已经下载的数据大小
*/
@property (nonatomic, assign) NSInteger currentSize;
/**
* 句柄指针
*/
@property (strong, nonatomic)NSFileHandle *handle;
/**
* 创建网络请求对象
*/
@property (strong, nonatomic)NSURLSessionDataTask *dataTask;
@end
@implementation ViewController
#pragma MARK - 懒加载
- (NSURLSessionDataTask *)dataTask{
if (!_dataTask) {
//01 确定资源路径
// NSURL *url = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"];
NSURL *url =[NSURL URLWithString:@"http://221.226.153.90:9097/zentao/bug-view-7649.html"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//+ 设置请求头信息(告诉对象只下载某一部分数据《Range》)
/**
Range:
bytes=0-100
bytes=-100 从文件开始到100个字节
bytes=100-400从文件的100个字节开始到文件的400个字节
bytes=400- 从文件的400个字节开始一直到文件的结尾
*/
NSString *header=[NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:header forKey:@"Range"];
NSLog(@"请求下载的数据范围:%@",header);
//03 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建下载请求的task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
_dataTask = dataTask;
}
return _dataTask;
}
#pragma---------------------------------------
#pragma MARK - 操作
//开始下载
- (IBAction)startBtnClick:(id)sender {
//05 发送请求
[self.dataTask resume];
NSLog(@"开始下载--------");
}
//暂停下载(是可以恢复下载的)
- (IBAction)suspenBtnClick:(id)sender {
[self.dataTask suspend];
NSLog(@"暂停下载--------");
}
//取消下载(不能恢复下载)
- (IBAction)cancleBtnClick:(id)sender {
NSLog(@"取消下载--------");
//说明:点击取消之后dataTask就已经结束了(可以在代理方法:didCompleteWithError中打印错误信息,进行验证)
[self.dataTask cancel];
//手动清空dataTask
self.dataTask = nil;
}
//恢复下载
- (IBAction)resumeBtnClick:(id)sender {
[self.dataTask resume];
NSLog(@"恢复下载--------");
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 01、方法说明:接收到响应的时候调用(每发一次请求就会调用该方法一次)
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"接收到服务器的响应:本次文件请求的数据大小---- %lld ----",response.expectedContentLength);
//获取本次文件请求的数据大小 文件的总大小 = 本次文件请求的数据大小 + 已经下载的文件数据大小
self.totalSize = response.expectedContentLength+self.currentSize;
NSLog(@"接收到服务器的响应:文件的总大小---- %zd ----",self.totalSize);
//00 获取存储沙盒的路径
NSString *fileName = response.suggestedFilename;
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接文件的存储路径(沙盒路径cache) + 文件名
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
//判断:判断是否是第一次发送请求下载(只有在第一次下载的时候才需要来创建一个空的文件)
if (self.currentSize == 0) {
//01 创建空文件
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
}
//02 创建文件句柄(默认指向文件的开头)
self.handle = [NSFileHandle fileHandleForWritingAtPath:fullPath];
//调整:移动文件句柄指针指向文件的末尾
[self.handle seekToEndOfFile];
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
/**
* 02、方法说明:接收到服务器返回数据的时候调用 可能会调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.handle writeData:data];
//计算文件的下载进度并显示 = 已经下载的数据/文件的总大小
self.currentSize += data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
}
/**
* 03、方法说明:下载完成或者是失败的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {//下载成功
[self.handle closeFile];
}else{//下载失败
NSLog(@"didCompleteWithError --- %@",error);
}
}
/**
文件句柄(指针):NSFileHandle(边接收数据,边写入到沙盒中)
特点:句柄在写数据的时候边写数据边移动位置
使用步骤:
1)创建空的文件
2)创建文件句柄指针指向该文件
3)当接收到数据的时候,使用句柄来写数据
4)当所有的数据写完,应该关闭句柄指针
*/
@end
- 文件
句柄指针
实现离线断点下载
// ViewController.m
// 25-文件的下载
//
// Created by mac2016 on 2020/5/31.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
#define kxFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"111.mp4"]
#define kxSizeFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"111.111"]
@interface ViewController ()<NSURLSessionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/**
* 获的文件的总大小
*/
@property (assign, nonatomic)NSInteger totalSize;
/**
* 当前已经下载的数据大小
*/
@property (nonatomic, assign) NSInteger currentSize;
/**
* 句柄指针
*/
@property (strong, nonatomic)NSFileHandle *handle;
/**
* 创建网络请求对象
*/
@property (strong, nonatomic)NSURLSessionDataTask *dataTask;
/**
* 创建网络请求对象
*/
@property (strong, nonatomic)NSURLSession *session;
@end
@implementation ViewController
#pragma MARK - 懒加载
- (NSURLSessionDataTask *)dataTask{
if (!_dataTask) {
//01 确定资源路径
// NSURL *url = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"];
NSURL *url =[NSURL URLWithString:@"http://221.226.153.90:9097/zentao/bug-view-7649.html"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//+ 设置请求头信息(告诉对象只下载某一部分数据《Range》)
/**
Range:
bytes=0-100
bytes=-100 从文件开始到100个字节
bytes=100-400从文件的100个字节开始到文件的400个字节
bytes=400- 从文件的400个字节开始一直到文件的结尾
*/
NSString *header=[NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:header forKey:@"Range"];
NSLog(@"请求下载的数据范围:%@",header);
//03 创建会话对象 设置代理
// NSURLSession *session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建下载请求的task
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
_dataTask = dataTask;
}
return _dataTask;
}
- (NSURLSession *)session{
if (!_session) {
_session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
#pragma---------------------------------------
- (void)viewDidLoad{
[super viewDidLoad];
//拿到之前下载的文件数据的大小 self.currentSize = 沙盒中文件数据的大小
//01 得到沙盒中已经下载的文件的属性
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:kxFullPath error:nil];
//获取沙盒路径kxFullPath下,已经下载文件的总大小 fileInfo[@"NSFileSize"];
NSLog(@"fileInfo - %@",fileInfo);
// self.currentSize = [fileInfo[@"NSFileSize"] integerValue];
self.currentSize = [fileInfo fileSize];
//处理进度信息 = 已下载文件的大小/文件的总大小;
//尝试读取沙盒中111.111文件的信息(文件的总大小)
NSData *sizeData = [NSData dataWithContentsOfFile:kxSizeFullPath];
self.totalSize = [[[NSString alloc] initWithData:sizeData encoding:NSUTF8StringEncoding] integerValue];
if (self.totalSize!=0) {
NSLog(@"进度信息:%f",1.0 * self.currentSize/self.totalSize);
self.progressView.progress = 1.0 * self.currentSize/self.totalSize;
}
//懒加载session的好处:要是还有其他的网络请求可以以下操作:
// self.session dataTaskWithRequest:<#(nonnull NSURLRequest *)#>
}
#pragma MARK - 操作
//开始下载
- (IBAction)startBtnClick:(id)sender {
//05 发送请求
[self.dataTask resume];
NSLog(@"开始下载--------");
}
//暂停下载(是可以恢复下载的)
- (IBAction)suspenBtnClick:(id)sender {
[self.dataTask suspend];
NSLog(@"暂停下载--------");
}
//取消下载(不能恢复下载)
- (IBAction)cancleBtnClick:(id)sender {
NSLog(@"取消下载--------");
//说明:点击取消之后dataTask就已经结束了(可以在代理方法:didCompleteWithError中打印错误信息,进行验证)
[self.dataTask cancel];
//手动清空dataTask
self.dataTask = nil;
}
//恢复下载
- (IBAction)resumeBtnClick:(id)sender {
[self.dataTask resume];
NSLog(@"恢复下载--------");
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 01、方法说明:接收到响应的时候调用(每发一次请求就会调用该方法一次)
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"接收到服务器的响应:本次文件请求的数据大小---- %lld ----",response.expectedContentLength);
//获取本次文件请求的数据大小 文件的总大小 = 本次文件请求的数据大小 + 已经下载的文件数据大小
self.totalSize = response.expectedContentLength+self.currentSize;
NSLog(@"接收到服务器的响应:文件的总大小---- %zd ----",self.totalSize);
//把文件的总大小信息保存,写入到沙盒
NSData *sizeData = [[NSString stringWithFormat:@"%zd",self.totalSize] dataUsingEncoding:NSUTF8StringEncoding];
[sizeData writeToFile:kxSizeFullPath atomically:YES];
//00 获取存储沙盒的路径
//拼接文件的存储路径(沙盒路径cache) + 文件名
//判断:判断是否是第一次发送请求下载(只有在第一次下载的时候才需要来创建一个空的文件)
if (self.currentSize == 0) {
//01 创建空文件
[[NSFileManager defaultManager] createFileAtPath:kxFullPath contents:nil attributes:nil];
}
//02 创建文件句柄(默认指向文件的开头)
self.handle = [NSFileHandle fileHandleForWritingAtPath:kxFullPath];
//调整:移动文件句柄指针指向文件的末尾
[self.handle seekToEndOfFile];
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
/**
* 02、方法说明:接收到服务器返回数据的时候调用 可能会调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.handle writeData:data];
//计算文件的下载进度并显示 = 已经下载的数据/文件的总大小
self.currentSize += data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
}
/**
* 03、方法说明:下载完成或者是失败的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {//下载成功
[self.handle closeFile];
}else{//下载失败
NSLog(@"didCompleteWithError --- %@",error);
}
}
/**
文件句柄(指针):NSFileHandle(边接收数据,边写入到沙盒中)
特点:句柄在写数据的时候边写数据边移动位置
使用步骤:
1)创建空的文件
2)创建文件句柄指针指向该文件
3)当接收到数据的时候,使用句柄来写数据
4)当所有的数据写完,应该关闭句柄指针
*/
@end
-
输出流
实现离线断点下载
输出流
:(可以理解为水管)
使用步骤:
1)创建输出流,打开输出流
2)当接收到服务器返回的数据的时候,使用输出流来写数据
3)当所有的数据写完的时候,应该关闭输出流
// ViewController.m
// 25-文件的下载
//
// Created by mac2016 on 2020/5/31.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
#define kxFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"111.mp4"]
#define kxSizeFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"111.111"]
@interface ViewController ()<NSURLSessionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
/**
* 获的文件的总大小
*/
@property (assign, nonatomic)NSInteger totalSize;
/**
* 当前已经下载的数据大小
*/
@property (nonatomic, assign) NSInteger currentSize;
/**
* 句柄
*/
//@property (strong, nonatomic)NSFileHandle *handle;
/**
* 输出流
*/
@property (strong, nonatomic)NSOutputStream *outputStream;
/**
* 创建网络请求对象
*/
@property (strong, nonatomic)NSURLSessionDataTask *dataTask;
/**
* 创建网络请求对象
*/
@property (strong, nonatomic)NSURLSession *session;
@end
@implementation ViewController
#pragma MARK - 懒加载
- (NSURLSessionDataTask *)dataTask{
if (!_dataTask) {
//01 确定资源路径
// NSURL *url = [NSURL URLWithString:@"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3984473917,238095211&fm=26&gp=0.jpg"];
NSURL *url =[NSURL URLWithString:@"http://221.226.153.90:9097/zentao/bug-view-7649.html"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//+ 设置请求头信息(告诉对象只下载某一部分数据《Range》)
/**
Range:
bytes=0-100
bytes=-100 从文件开始到100个字节
bytes=100-400从文件的100个字节开始到文件的400个字节
bytes=400- 从文件的400个字节开始一直到文件的结尾
*/
NSString *header=[NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:header forKey:@"Range"];
NSLog(@"请求下载的数据范围:%@",header);
//03 创建会话对象 设置代理
//04 创建下载请求的task
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
_dataTask = dataTask;
}
return _dataTask;
}
- (NSURLSession *)session{
if (!_session) {
_session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
#pragma---------------------------------------
- (void)viewDidLoad{
[super viewDidLoad];
//拿到之前下载的文件数据的大小 self.currentSize = 沙盒中文件数据的大小
//01 得到沙盒中已经下载的文件的属性
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:kxFullPath error:nil];
//获取沙盒路径kxFullPath下,已经下载文件的总大小 fileInfo[@"NSFileSize"];
NSLog(@"fileInfo - %@",fileInfo);
// self.currentSize = [fileInfo[@"NSFileSize"] integerValue];
self.currentSize = [fileInfo fileSize];
//处理进度信息 = 已下载文件的大小/文件的总大小;
//尝试读取沙盒中111.111文件的信息(文件的总大小)
NSData *sizeData = [NSData dataWithContentsOfFile:kxSizeFullPath];
self.totalSize = [[[NSString alloc] initWithData:sizeData encoding:NSUTF8StringEncoding] integerValue];
if (self.totalSize!=0) {
NSLog(@"进度信息:%f",1.0 * self.currentSize/self.totalSize);
self.progressView.progress = 1.0 * self.currentSize/self.totalSize;
}
}
#pragma MARK - 操作
//开始下载
- (IBAction)startBtnClick:(id)sender {
//05 发送请求
[self.dataTask resume];
NSLog(@"开始下载--------");
}
//暂停下载(是可以恢复下载的)
- (IBAction)suspenBtnClick:(id)sender {
[self.dataTask suspend];
NSLog(@"暂停下载--------");
}
//取消下载(不能恢复下载)
- (IBAction)cancleBtnClick:(id)sender {
NSLog(@"取消下载--------");
//说明:点击取消之后dataTask就已经结束了(可以在代理方法:didCompleteWithError中打印错误信息,进行验证)
[self.dataTask cancel];
//手动清空dataTask
self.dataTask = nil;
}
//恢复下载
- (IBAction)resumeBtnClick:(id)sender {
[self.dataTask resume];
NSLog(@"恢复下载--------");
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 01、方法说明:接收到响应的时候调用(每发一次请求就会调用该方法一次)
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"接收到服务器的响应:本次文件请求的数据大小---- %lld ----",response.expectedContentLength);
//获取本次文件请求的数据大小 文件的总大小 = 本次文件请求的数据大小 + 已经下载的文件数据大小
self.totalSize = response.expectedContentLength+self.currentSize;
NSLog(@"接收到服务器的响应:文件的总大小---- %zd ----",self.totalSize);
//把文件的总大小信息保存,写入到沙盒
NSData *sizeData = [[NSString stringWithFormat:@"%zd",self.totalSize] dataUsingEncoding:NSUTF8StringEncoding];
[sizeData writeToFile:kxSizeFullPath atomically:YES];
// 1)创建输出流,打开输出流
/**
*
*
* 参数说明
*
* 第一个参数:指向文件的路径(如果指定路径的文件不存在,那么输出流会自动创建一个空文件)
* 第二个参数:表示是否要进行数据追加(YES:追加|NO:覆盖)
*
*/
NSOutputStream *outputStream = [[NSOutputStream alloc] initToFileAtPath:kxFullPath append:YES];
[outputStream open];
self.outputStream = outputStream;
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
/**
* 02、方法说明:接收到服务器返回数据的时候调用 可能会调用多次
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
// 2)当接收到服务器返回的数据的时候,使用输出流来写数据
/**
* 参数说明
*
* 第一个参数:写入的数据格式是bytes : data.bytes
* 第二个参数:写入数据的长度maxLength : data.length
*
*/
[self.outputStream write:data.bytes maxLength:data.length];
//计算文件的下载进度并显示 = 已经下载的数据/文件的总大小
self.currentSize += data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
}
/**
* 03、方法说明:下载完成或者是失败的时候调用
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (!error) {//下载成功
// 3)当所有的数据写完的时候,应该关闭输出流
[self.outputStream close];
}else{//下载失败
NSLog(@"didCompleteWithError --- %@",error);
}
}
/**
输出流:(可以理解为水管)
使用步骤:
1)创建输出流,打开输出流
2)当接收到服务器返回的数据的时候,使用输出流来写数据
3)当所有的数据写完的时候,应该关闭输出流
*/
@end
-
NSURLSessionDownloadTask
文件下载
1、代理方式下载
2、block方式下载
// ViewController.m
// 31-文件下载(downloadTask)
//
// Created by mac2016 on 2020/6/3.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDownloadDelegate>
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self downLoadTask_delegate];
}
#pragma---------------------------------------
/**
* 优点:能够监听文件的下载进度,适合大文件下载
*/
//downLoadTask delegate 方式下载文件
- (void)downLoadTask_delegate{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591191019405&di=9b01ca1c8414c970337a9377c13eab28&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F68%2F61%2F300000839764127060614318218_950.jpg"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象
NSURLSession *session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]] ;
NSURLSessionDownloadTask *downLoadTask = [session downloadTaskWithRequest:request];
[downLoadTask resume];
}
#pragma MARK - NSURLSessionDownloadDelegate
//01 网沙盒中写数据的时候调用
/**
* 参数说明
*
* 第一个参数:bytesWritten:表示本次写入的数据大小
* 第二个参数:totalBytesWritten:表示写入数据的总大小
* 第三个参数:totalBytesExpectedToWrite:表示文件的总大小
*
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
NSLog(@"%f",1.0 * totalBytesWritten/totalBytesExpectedToWrite);
}
//02 下载完成的时候调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
//将文件转移到安全的地方
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//文件名
NSString *fileName = downloadTask.response.suggestedFilename;
NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];
NSURL *cacheURL = [NSURL fileURLWithPath:fullPath];
NSLog(@"cacheURL:%@",cacheURL);
//剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:cacheURL error:nil];
NSLog(@"fullPath:%@",fullPath);
}
//03 整个结束或失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
}
#pragma---------------------------------------
/**
缺点:无法监听文件下载的进度,仅适合下载小文件
*/
//downLoadTask BLOCK 方式下载文件
- (void)downLoadTask_typeBlock{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591191019405&di=9b01ca1c8414c970337a9377c13eab28&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F68%2F61%2F300000839764127060614318218_950.jpg"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 创建downloadTask
/**
* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:请求回调
* 第三个参数:completionHandler
* location 位置:(NSURL类型的)文件的位置(其内部已经实现了边接收数据边写入到沙盒的操作)
* response:响应头信息
*/
NSURLSessionDownloadTask *downLoadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//默认已经把下载的文件写入到沙盒的tmp临时文件中:/(随时可能被删除)
NSLog(@"location:%@",location);
//将文件转移到安全的地方
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = response.suggestedFilename;
NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];
NSURL *cacheURL = [NSURL fileURLWithPath:fullPath];
NSLog(@"cacheURL:%@",cacheURL);
[[NSFileManager defaultManager] moveItemAtURL:location toURL:cacheURL error:nil];
}];
//05 发送请求
[downLoadTask resume];
}
@end
- 文件下载
NSURLSessionDownloadTask
断点下载
NSURLSessionDownloadTask:
不能实现离线断点下载
// ViewController.m
// 31-文件下载(downloadTask)
//
// Created by mac2016 on 2020/6/3.
// Copyright © 2020 xuly. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDownloadDelegate>
/**
* <#注释#>
*/
@property (strong, nonatomic)NSURLSessionDownloadTask *downLoadTask;
/**
* <#注释#>
*/
@property (strong, nonatomic)NSData *resumeData;
/**
* <#注释#>
*/
@property (strong, nonatomic)NSURLSession *session;
@end
@implementation ViewController
- (NSURLSession *)session{
if (!_session) {
_session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]] ;
}
return _session;
}
- (NSURLSessionDownloadTask *)downLoadTask{
if (!_downLoadTask) {
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591191019405&di=9b01ca1c8414c970337a9377c13eab28&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F68%2F61%2F300000839764127060614318218_950.jpg"];
// NSURL *url = [NSURL URLWithString:@"http://221.226.153.90:9097/zentao/bug-view-7649.html"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象
_downLoadTask = [self.session downloadTaskWithRequest:request];
}
return _downLoadTask;
}
//开始
- (IBAction)startBtnClick:(id)sender {
[self.downLoadTask resume];
}
//暂停
- (IBAction)suspendBtnClick:(id)sender {
[self.downLoadTask suspend];
}
//取消
- (IBAction)cancleBtnClick:(id)sender {
//取消普通的取消操作是不可以恢复的
// [self.downLoadTask cancel];
//这是取消操作可以恢复
//resumeData:表示可以用来恢复下载的数据 (并不是沙盒中已经下载好的数据)
[self.downLoadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
self.resumeData = resumeData;
}];
self.downLoadTask = nil;
}
//恢复
- (IBAction)resumeBtnClick:(id)sender {
//暂停 -> 恢复
if (self.resumeData) {
//取消 -> 恢复
//在恢复下载的时候,判断是否又可以用来进行恢复下载的数据,如果有那么就根据该数据创建一个网络请求
self.downLoadTask = [self.session downloadTaskWithResumeData:self.resumeData];
self.resumeData = nil;
}
[self.downLoadTask resume];
}
#pragma MARK - NSURLSessionDownloadDelegate
//01 网沙盒中写数据的时候调用
/**
* 参数说明
*
* 第一个参数:bytesWritten:表示本次写入的数据大小
* 第二个参数:totalBytesWritten:表示写入数据的总大小
* 第三个参数:totalBytesExpectedToWrite:表示文件的总大小
*
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
NSLog(@"%f",1.0 * totalBytesWritten/totalBytesExpectedToWrite);
}
//02 下载完成的时候调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
//将文件转移到安全的地方
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//文件名
NSString *fileName = downloadTask.response.suggestedFilename;
NSString *fullPath = [cachePath stringByAppendingPathComponent:fileName];
NSURL *cacheURL = [NSURL fileURLWithPath:fullPath];
NSLog(@"cacheURL:%@",cacheURL);
//剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:cacheURL error:nil];
NSLog(@"fullPath:%@",fullPath);
}
//03 整个结束或失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
}
- 文件上传
文件上传的步骤:
1、确定上传的路径
2、创建一个“可变”的请求对象
3、修改请求方法为POST
+“设置请求头信息,告诉服务器这是一个文件上传请求”
+"按照固定的格式来拼接数据"
4、设置请求体信息(文件参数)
5、创建会话对象
6、根据会话对象来创建uploadTask
7、执行uploadTask
发送请求上传文件
- 设置格式
/**
1、设置请求头信息
Content-Type:multipart/form-data;boundary=----WebKitFormBoundaryATJp9y6FGSNtJKNW
2、按照固定的格式来拼接数据
------WebKitFormBoundaryATJp9y6FGSNtJKNW
Content-Disposition:form-data;name="file";filename="123.png"
Content-Type:image/png
文件的数据
------WebKitFormBoundaryATJp9y6FGSNtJKNW
Content-Disposition:form-data;name="username"
abcdef
------WebKitFormBoundaryATJp9y6FGSNtJKNW
*/
//简化
/**
分隔符 ----WebKitFormBoundaryATJp9y6FGSNtJKNW
文件参数
--分隔符
Content-Disposition:form-data;name="file";filename="123.png"
Content-Type:image/png
空行
文件数据
非文件参数
--分隔符
Content-Disposition:form-data;name="username"
空行
abcdef
结尾标识
--分隔符--
*/
- 示例代码程序
#import "ViewController.h"
#define Kboundary @"----WebKitFormBoundaryATJp9y6FGSNtJKNW"
#define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()<NSURLSessionDataDelegate>
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self upload_delegate];
}
- (void)upload_delegate{
// 1、确定上传的路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
// 2、创建一个“可变”的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 3、修改请求方法为POST
request.HTTPMethod = @"POST";
//
// +“设置请求头信息,告诉服务器这是一个文件上传请求”
// Content-Type:multipart/form-data;boundary=----WebKitFormBoundaryATJp9y6FGSNtJKNW
[request setValue:[NSString stringWithFormat:@"multipart/form-data;boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
// +"按照固定的格式来拼接数据"
//
// 4、设置请求体信息(文件参数)
// 5、创建会话对象
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 6、根据会话对象来创建uploadTask
/**
* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:本应该放在请求体中的信息
* 第三个参数:
*
*/
NSData *fromData = [self bodyData];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:fromData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 7、执行uploadTask发送请求上传文件
[uploadTask resume];
}
#pragma---------------------------------------
#pragma MARK - NSURLSessionDataDelegate
/**
* 参数说明
*
* 第一个参数:bytesSent 本次上传的文件数据的大小
* 第二个参数:totalBytesSent 已经上传的文件数据的总大小
* 第三个参数:totalBytesExpectedToSend 要上传文件的总大小
*
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
NSLog(@"%f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}
#pragma---------------------------------------
//block : 不能监听上传文件的进度x
- (void)upload_block{
// 1、确定上传的路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
// 2、创建一个“可变”的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 3、修改请求方法为POST
request.HTTPMethod = @"POST";
//
// +“设置请求头信息,告诉服务器这是一个文件上传请求”
// Content-Type:multipart/form-data;boundary=----WebKitFormBoundaryATJp9y6FGSNtJKNW
[request setValue:[NSString stringWithFormat:@"multipart/form-data;boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
// +"按照固定的格式来拼接数据"
//
// 4、设置请求体信息(文件参数)
// 5、创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
// 6、根据会话对象来创建uploadTask
/**
* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:本应该放在请求体中的信息
* 第三个参数:
*
*/
NSData *fromData = [self bodyData];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:fromData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 7、执行uploadTask发送请求上传文件
[uploadTask resume];
}
- (NSData *)bodyData{
NSMutableData *data = [NSMutableData data];
// 文件参数
// --分隔符
// Content-Disposition:form-data;name="file";filename="123.png"
// Content-Type:image/png
// 空行
// 文件数据
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
//name:file 服务器规定
//filename:该文件上传服务器之后的名称
//username|pwd
[data appendData:[@"Content-Disposition:form-data;name=\"file\";filename=\"123.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
//Content-Type:要上传文件的二进制数据类型
//MIMType:组成:大类型/小类型 如:image/png
[data appendData:[@"Content-Type:image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
[data appendData:KNewLine];
NSData *imageData = [NSData dataWithContentsOfFile:@"/Users/nana/Desktop/123.png"];
[data appendData:imageData];
[data appendData:KNewLine];
// 非文件参数
// --分隔符
// Content-Disposition:form-data;name="username"
// 空行
// abcdef
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
[data appendData:[@"Content-Disposition:form-data;name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
[data appendData:KNewLine];
[data appendData:[@"abcdef" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KNewLine];
// 结尾标识
// --分隔符--
[data appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
@end
- 获得文件的
MIMEType
#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//01.给本地文件发送请求,在NSURLResponse中获取MIMEType类型
// [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL fileURLWithPath:@"/Users/nana/Desktop/123.png"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//
// NSLog(@"mimeType:%@",response.MIMEType);
//
//
// }] resume];
//
//02.C语言方式获取
NSString *mimeType = [self mimeTypeForFileAtPath:@"/Users/nana/Desktop/123.png"];
NSLog(@"mimeType:%@",mimeType);
//03.通用的二进制数据类型:application/octet-stream
}
- (NSString *)mimeTypeForFileAtPath:(NSString *)path{
if (![[[NSFileManager alloc] init] fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef _Nonnull)([path pathExtension]), NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
CFRelease(UTI);
if (!MIMEType) {
return @"application/octet-stream";
}
return (__bridge NSString *)(MIMEType);
}
@end
以上内容,为本人结合相关课程的总结iOS网络编程的相关的知识点,如有错误敬请批正......