NSURLConnection
先了解一些iOS开发中网络开发相关的常用类
NSURL
请求地址
NSURLRequest
一个NSURLRequest对象就代表一个请求,它包含的信息有:
- 一个NSURL对象
- 请求方法、请求头、请求体
- 请求超时
- ...
NSMutableURLRequest
NSURLRequest的子类,内容可变
NSURLConnection(目前已弃用)
- 负责发送请求,建立客户端和服务器的连接
- 发送数据给服务器,并收集来自服务器的响应数据
使用NSURLConnection发送请求的步骤很简单
(1)创建一个NSURL对象,设置请求路径(设置请求路径)
(2)传入NSURL创建一个NSURLRequest对象,设置请求头和请求体(创建请求对象)
(3)使用NSURLConnection发送NSURLRequest(发送请求)
请求实例
请求:请求头(NSURLRequest默认包含) + 请求体(Get没有请求体)
响应:相应头(真实类型->NSHTTPURLResponse)+ 响应体(要解析的数据)
发送同步Get请求
- (void)httpGetSynchronous
{
//GET 没有请求体
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//创建请求对象(无需设置请求头,会使用默认的请求头)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//发送请求
//NSURLResponse *response = nil;
//真实类型其实是:
NSHTTPURLResponse *response = nil;
/* 同步Http请求,使用这个方法会阻塞当前线程
* 请求服务器返回相应体信息 NSData
* 参数1:请求对象
* 参数2:相应头信息,用来接收服务器返回信息,传的是地址
* 参数3:错误信息
*/
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(@"服务器返回的信息为:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSLog(@"服务器返回的状态为:%ld",response.statusCode);
/*
2016-12-09 16:04:31.538219 NSURLConnectionTest[6174:2208758] 服务器返回的信息为:{"success":"登录成功"}
2016-12-09 16:04:31.538430 NSURLConnectionTest[6174:2208758] 服务器返回的状态为:200
*/
}
发送异步Get请求
- (void)httpGetAsynchronous
{
//GET 没有请求体
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//创建请求对象(无需设置请求头,会使用默认的请求头)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
/* 异步Http请求,使用这个方法会在子线程中执行
* 参数1:请求对象
* 参数2:队列,决定代码块调用线程
* 参数3:请求完成(成功、失败都算完成)后的回调
* response 相应头
* data 相应体
* connectionError 错误信息
*/
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"服务器返回的信息为:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSHTTPURLResponse *httpRespose = (NSHTTPURLResponse*)response;
NSLog(@"服务器返回的状态为:%ld",httpRespose.statusCode);
}];
//NSLog(@"服务器返回的信息为:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
//NSLog(@"服务器返回的状态为:%ld",response.statusCode);
/*
2016-12-09 16:04:31.538219 NSURLConnectionTest[6174:2208758] 服务器返回的信息为:{"success":"登录成功"}
2016-12-09 16:04:31.538430 NSURLConnectionTest[6174:2208758] 服务器返回的状态为:200
*/
}
通过代理发送Get请求
@interface ViewController () <NSURLConnectionDataDelegate>//遵守NSURLConnectionDataDelegate协议,注意,不是NSURLConnectionDelegate
@property(strong,nonatomic)NSMutableData *reciveData;
@end
@implementation ViewController
- (NSMutableData *)reciveData
{
if(_reciveData == nil)
{
_reciveData = [NSMutableData data];
}
return _reciveData;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self httpGetWithDelegate];
}
- (void)httpGetWithDelegate
{
//GET 没有请求体
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//创建请求对象(无需设置请求头,会使用默认的请求头)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//设置代理的方法有多种:
//1.类方法,设置代理并发送请求
//[NSURLConnection connectionWithRequest:request delegate:self];
//2.初始化方法,设置代理并发送请求
//[[NSURLConnection alloc]initWithRequest:request delegate:self];
//3.初始化方法,设置代理,只有当startImmediately为YES才会发送请求
[[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
//如果startImmediately设置为NO,要发送请求还需调用:
//[connection start];
//如果要取消请求调用:
//[connection cancel];
}
//响应头
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(@"%s",__func__);
NSHTTPURLResponse *httpRespose = (NSHTTPURLResponse*)response;
NSLog(@"服务器返回的状态为:%ld",httpRespose.statusCode);
}
//响应体
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"%s",__func__);
[self.reciveData appendData:data];//拼接数据,如果返回的数据过大,这个方法会调用多次,所以需要一个变量来拼接数据
}
//错误信息
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"%s",__func__);
}
//请求完成
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"%s",__func__);
NSLog(@"服务器返回的信息为:%@",[[NSString alloc] initWithData:self.reciveData encoding:NSUTF8StringEncoding]);
}
@end
以上代码执行的结果:
2016-12-09 17:40:30.571966 NSURLConnectionTest[6215:2229156] -[ViewController connection:didReceiveResponse:]
2016-12-09 17:40:30.572058 NSURLConnectionTest[6215:2229156] 服务器返回的状态为:200
2016-12-09 17:40:30.572186 NSURLConnectionTest[6215:2229156] -[ViewController connection:didReceiveData:]
2016-12-09 17:40:30.572361 NSURLConnectionTest[6215:2229156] -[ViewController connectionDidFinishLoading:]
2016-12-09 17:40:30.572410 NSURLConnectionTest[6215:2229156] 服务器返回的信息为:{"success":"登录成功"}
发送异步Post请求
- (void)httpPost
{
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//创建请求对象(无需设置请求头,会使用默认的请求头)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//修改请求方法,默认为GET
request.HTTPMethod = @"POST";//POST必须大写
//设置请求体信息
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
//输出:2016-12-09 22:10:36.066 NSURLConnectionTest[4950:164558] {"success":"登录成功"}
}];
}
NSURLRequest的一些方法
请求超时时间,比如10秒
request.timeoutInterval = 10;
重设请求头
比如请求头如下
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4
Connection:keep-alive
Host:120.25.226.186:32812
Referer:http://120.25.226.186:32812/
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
可以通过如下方法设置请求头,修改头文件键对应的值即可
[request setValue:@"iOS10.1" forHTTPHeaderField:@"User-Agent"];
天朝文字的特殊处理
注意:有个路径中如果含有傻逼的中文字符,还需要做特殊处理
//Get
- (void)httpGetAsynchronousWithTianchaoFont
{
NSString *str = @"http://120.25.226.186:32812/login2?username=小码哥&pwd=520it&type=JSON";
NSLog(@"转码前:%@",str);//http://120.25.226.186:32812/login2?username=小码哥&pwd=123&type=JSON
str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"转码后:%@",str);//http://120.25.226.186:32812/login2?username=%E5%B0%8F%E7%A0%81%E5%93%A5&pwd=123&type=JSON
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
if(connectionError)
{
NSLog(@"connectionError: %@",connectionError);
}
NSLog(@"服务器返回的信息为:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
NSHTTPURLResponse *httpRespose = (NSHTTPURLResponse*)response;
NSLog(@"服务器返回的状态为:%ld",httpRespose.statusCode);
}];
}
//Post
- (void)httpPostWithTianchaoFuckFont
{
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login2"];//如果url中还有中文字符,也需要转码
//创建请求对象(无需设置请求头,会使用默认的请求头)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//修改请求方法,默认为GET
request.HTTPMethod = @"POST";//POST必须大写
//设置请求体信息
request.HTTPBody = [@"username=小码哥&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];//请求体则不在需要专门转码,这个方法中会处理
//发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
//输出:2016-12-09 22:10:36.066 NSURLConnectionTest[4950:164558] {"success":"登录成功"}
}];
//请求超时时间,比如10秒
request.timeoutInterval = 10;
//设置请求头信息
[request setValue:@"iOS10.1" forHTTPHeaderField:@"User-Agent"];
}
小文件下载的实例1:
+ (void)downloadSmallFile
{
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_13.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileFullPath = [path stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"%@",fileFullPath);
[data writeToFile:fileFullPath atomically:YES];
}];
}
小文件下载的实例2:
#import "ViewController.h"
@interface ViewController () <NSURLConnectionDataDelegate>
@property(assign,nonatomic)NSInteger downloadTotal;
@property(strong,nonatomic)NSMutableData *recivedData;
@end
@implementation ViewController
- (NSMutableData *)recivedData
{
if(!_recivedData)
{
_recivedData = [NSMutableData data];
}
return _recivedData;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//NSLog(@"%zd",response.expectedContentLength);
self.downloadTotal = response.expectedContentLength;//表示本次下载的数据长度
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//NSLog(@"%zd",data.length);
[self.recivedData appendData:data];
//NSLog(@"%zd----%zd",self.recivedData.length,self.downloadTotal);
NSLog(@"download progress:%zd",(NSInteger)(1.0 * self.recivedData.length / self.downloadTotal * 100));
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"下载完成");
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [path stringByAppendingPathComponent:@"3.mp4"];
NSLog(@"%@",fullPath);
[self.recivedData writeToFile:fullPath atomically:YES];
}
- (void)cancelDownload
{
//取消下载
[self.connection cancel];
}
@end
大文件下载
上面的代码虽然能做到文件的下载,但他会将网络加载的数据一直缓存在内存中释放不掉,如果是下载轻量级的文件还可以采取,如果是大数据的文件就不能用上面的方法
这个时候要用到NSFileManager和NSFileHandle
首先用NSFileManager创建一个空文件,NSFileHandle类中得方法可以对文件进行基本的读写,偏移量的操作。我们使用NSFileHandle偏移倒文件的末尾,然后向末尾追加数据。在文件下载完毕以后,记得要关闭NSFileHandler
#import "ViewController.h"
@interface ViewController () <NSURLConnectionDataDelegate>
@property(assign,nonatomic)NSInteger downloadTotal;
@property(assign,nonatomic)NSInteger downloadDataLength;
@property(strong,nonatomic)NSString *fullFilePath;
@property(strong,nonatomic)NSFileHandle *fileHandler;
@end
@implementation ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//NSLog(@"%zd",response.expectedContentLength);
self.downloadTotal = response.expectedContentLength;//表示本次下载的数据长度
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
self.fullFilePath = [path stringByAppendingPathComponent:@"3.mp4"];
NSLog(@"%@",self.fullFilePath);
[[NSFileManager defaultManager] createFileAtPath:self.fullFilePath contents:nil attributes:nil];
self.fileHandler = [NSFileHandle fileHandleForWritingAtPath:self.fullFilePath];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//将指针移动到文件末尾位置
[self.fileHandler seekToEndOfFile];
[self.fileHandler writeData:data];
self.downloadDataLength += data.length;
NSLog(@"download progress:%zd",(NSInteger)(1.0 * self.downloadDataLength / self.downloadTotal * 100));
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.fileHandler closeFile];
self.fileHandler = nil;
NSLog(@"下载完成");
}
- (void)cancelDownload
{
//取消下载
[self.connection cancel];
}
@end
支持断点续传的下载实例
#import "ViewController.h"
@interface ViewController () <NSURLConnectionDataDelegate>
@property (strong, nonatomic) IBOutlet UIProgressView *progressView;
@property(assign,nonatomic)NSInteger downloadTotal;
@property(assign,nonatomic)NSInteger downloadDataLength;
@property(strong,nonatomic)NSString *fullFilePath;
@property(strong,nonatomic)NSFileHandle *fileHandler;
@property(strong,nonatomic)NSURLConnection *connection;
@end
@implementation ViewController
- (IBAction)startDownload:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//实现断点续传的关键是设置请求头,在请求头设置要获得的数据的范围
//表示头500个字节:Range:bytes=0-499
//表示第二个500字节:Range:bytes=500-999
//表示最后500个字节:Range:bytes=-500
//表示500字节以后的范围:Range:bytes=500-
NSString *lastRangeStr = [NSString stringWithFormat:@"bytes=%zd-",self.downloadDataLength];
[request setValue:lastRangeStr forHTTPHeaderField:@"Range"];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
- (IBAction)stopDownload:(id)sender {
[self.connection cancel];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//NSLog(@"%zd",response.expectedContentLength);
//self.downloadTotal = response.expectedContentLength + self.downloadDataLength;//表示本次下载的数据长度,如果这句写在return前,就必须每次执行获取本次下载长度再加上已经下载的长度,否则总量会出错
if(self.downloadTotal)return;
self.downloadTotal = response.expectedContentLength;//表示本次下载的数据长度
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
self.fullFilePath = [path stringByAppendingPathComponent:@"3.mp4"];
NSLog(@"%@",self.fullFilePath);
[[NSFileManager defaultManager] createFileAtPath:self.fullFilePath contents:nil attributes:nil];
self.fileHandler = [NSFileHandle fileHandleForWritingAtPath:self.fullFilePath];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.fileHandler seekToEndOfFile];
[self.fileHandler writeData:data];
self.downloadDataLength += data.length;
float progress = 1.0 * self.downloadDataLength / self.downloadTotal;
[self.progressView setProgress:progress];
NSLog(@"download progress:%zd",(NSInteger)(progress * 100));
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.fileHandler closeFile];
self.fileHandler = nil;
NSLog(@"下载完成");
}
- (void)cancelDownload
{
//取消下载
[self.connection cancel];
}
@end
使用NSOutputStream进行断点续传下载
#import "ViewController.h"
@interface ViewController () <NSURLConnectionDataDelegate>
@property (strong, nonatomic) IBOutlet UIProgressView *progressView;
@property(assign,nonatomic)NSInteger downloadTotal;
@property(assign,nonatomic)NSInteger downloadDataLength;
@property(strong,nonatomic)NSString *fullFilePath;
@property(strong,nonatomic)NSURLConnection *connection;
@property(strong,nonatomic)NSOutputStream *stream;
@end
@implementation ViewController
- (IBAction)startDownload:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_03.mp4"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//设置请求头
//表示头500个字节:Range:bytes=0-499
//表示第二个500字节:Range:bytes=500-999
//表示最后500个字节:Range:bytes=-500
//表示500字节以后的范围:Range:bytes=500-
NSString *lastRangeStr = [NSString stringWithFormat:@"bytes=%zd-",self.downloadDataLength];
[request setValue:lastRangeStr forHTTPHeaderField:@"Range"];
self.connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
}
- (IBAction)stopDownload:(id)sender {
[self.connection cancel];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if(self.downloadTotal)return;
self.downloadTotal = response.expectedContentLength;//表示本次下载的数据长度
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
self.fullFilePath = [path stringByAppendingPathComponent:@"3.mp4"];
NSLog(@"%@",self.fullFilePath);
/*
*参数1:文件的路径
*参数2:YES表示数据向后追加,NO表示不追加
*/
self.stream = [[NSOutputStream alloc]initToFileAtPath:self.fullFilePath append:YES];
//打开输出流
[self.stream open];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.stream write:data.bytes maxLength:data.length];//注意第一个参数要填data的bytes属性,因为要传的是const uint8_t *类型
self.downloadDataLength += data.length;
float progress = 1.0 * self.downloadDataLength / self.downloadTotal;
[self.progressView setProgress:progress];
NSLog(@"download progress:%zd",(NSInteger)(progress * 100));
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.stream close];
NSLog(@"下载完成");
}
- (void)cancelDownload
{
//取消下载
[self.connection cancel];
}
@end
文件上传
上传一个文件会产生如下所示的请求头和请求体
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:3369
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryxKActJ0tULkgRBtp
Host:120.25.226.186:32812
Origin:http://120.25.226.186:32812
Referer:http://120.25.226.186:32812/
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
Request Payload
------WebKitFormBoundaryxKActJ0tULkgRBtp
Content-Disposition: form-data; name="file"; filename="Math2.db"
Content-Type: application/octet-stream
------WebKitFormBoundaryxKActJ0tULkgRBtp
Content-Disposition: form-data; name="username"
123456
------WebKitFormBoundaryxKActJ0tULkgRBtp--
所有在Http请求的时候要严格按照上述格式来拼接
文件上传的步骤:
设置请求头
[request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];
设置请求体
非文件参数
--分割线\r\n
Content-Disposition: form-data; name="参数名"\r\n
\r\n
参数值
\r\n
文件参数
--分割线\r\n
Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
Content-Type: 文件的MIMEType\r\n
\r\n
文件数据
\r\n
参数结束的标记
--分割线--\r\n
//文件上传步骤
/*
1.设置请求头
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryjv0UfA04ED44AhWx
2.按照固定的格式拼接请求体的数据
------WebKitFormBoundaryjv0UfA04ED44AhWx
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png
------WebKitFormBoundaryjv0UfA04ED44AhWx
Content-Disposition: form-data; name="username"
123456
------WebKitFormBoundaryjv0UfA04ED44AhWx--
*/
//拼接请求体的数据格式
/*
拼接请求体
分隔符:----WebKitFormBoundaryjv0UfA04ED44AhWx
1)文件参数
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png(MIMEType:大类型/小类型)
空行
文件参数
2)非文件参数
--分隔符
Content-Disposition: form-data; name="username"
空行
123456
3)结尾标识
--分隔符--
*/
#import "ViewController.h"
#define Kboundary @"----WebKitFormBoundaryjv0UfA04ED44AhWx"
#define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self upload];
}
-(void)upload
{
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
//2.创建可变的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//3.设置请求方法
request.HTTPMethod = @"POST";
//4.设置请求头信息
//Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryjv0UfA04ED44AhWx
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
//5.拼接请求体数据
NSMutableData *fileData = [NSMutableData data];
//5.1 文件参数
/*
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png(MIMEType:大类型/小类型)
空行
文件参数
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
//name:file 服务器规定的参数
//filename:Snip20160225_341.png 文件保存到服务器上面的名称
//Content-Type:文件的类型
[fileData appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"IMG_2545.JPG\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:KNewLine];
UIImage *image = [UIImage imageNamed:@"Snip20160225_341"];
//UIImage --->NSData
//NSData *imageData = UIImagePNGRepresentation(image);
NSData *imageData = UIImageJPEGRepresentation(image, 1);
[fileData appendData:imageData];
[fileData appendData:KNewLine];
//5.2 非文件参数
/*
--分隔符
Content-Disposition: form-data; name="username"
空行
123456
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:KNewLine];
[fileData appendData:[@"123456" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
//5.3 结尾标识
/*
--分隔符--
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
//6.设置请求体
request.HTTPBody = fileData;
//7.发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//8.解析数据
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
}
@end
部分文件的MIMEType
获得MIMEType的途径:
1.使用NSURLConnection请求本地文件url
- (NSString*)getMimeTypeWithPath:(NSString*)path
{
NSURL *url = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
return response.MIMEType;
}
2.使用一个C语言的方法
#import <MobileCoreServices/MobileCoreServices.h>
...
- (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);
}
关于NSConnection和Runloop的关系
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self newThreadDelegate2];
}
-(void)delegate1
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=123&pwd=123&type=JSON"]];
//设置代理
//代理方法:默认是在主线程中调用的
NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
//设置代理方法在哪个线程中调用
//[NSOperationQueue alloc]init]] 开子线程
//[NSOperationQueue mainQueue] 不能这样设置
[connect setDelegateQueue:[[NSOperationQueue alloc]init]];
//[connect setDelegateQueue:[NSOperationQueue mainQueue]];
NSLog(@"-------");
}
-(void)delegate2
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=123&pwd=123&type=JSON"]];
//设置代理
//代理方法:默认是在主线程中调用的
NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[connect setDelegateQueue:[[NSOperationQueue alloc]init]];
//开始发送请求
[connect start];
NSLog(@"-------");
}
-(void)newThreadDelegate1
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=123&pwd=123&type=JSON"]];
//设置代理
//代理方法:默认是在主线程中调用的
//该方法内部其实会将connect对象作为一个source添加到当前的runloop中,指定运行模式为默认
NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
//设置代理方法在哪个线程中调用
[connect setDelegateQueue:[[NSOperationQueue alloc]init]];
//[[NSRunLoop currentRunLoop] runMode:UITrackingRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1000]];
[[NSRunLoop currentRunLoop]run];
NSLog(@"---%@----",[NSThread currentThread]);
});
}
-(void)newThreadDelegate2
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=123&pwd=123&type=JSON"]];
//设置代理
//代理方法:默认是在主线程中调用的
NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[connect setDelegateQueue:[[NSOperationQueue alloc]init]];
//开始发送请求
//如如果connect对象没有添加到runloop中,那么该方法内部会自动的添加到runloop
//注意:如果当前的runloop没有开启,那么该方法内部会自动获得当前线程对应的runloop对象并且开启
[connect start];
NSLog(@"---%@----",[NSThread currentThread]);
});
}
#pragma mark ----------------------
#pragma mark NSURLConnectionDataDelegate
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(@"didReceiveResponse---%@",[NSThread currentThread]);
}
@end