http请求原理,主要是post,首先post向服务器发送key,value格式的数据,一般都是一下字符串,例如:
NSDictionary *dic = @{@"key1":@"value1",@"key2":@"value2",@"key3":@"value3"};
//传递形式则是:
--AaB03x
Content-Disposition: form-data; name="key1"
value1
--AaB03x
Content-Disposition: form-data; name="key3"
value3
--AaB03x
Content-Disposition: form-data; name="key2"
value2
–AaB03x是分割符,如果单纯的传递字符串形式的,分隔符是每个系统确定的,例如ios,或者Android等SDK确定的,也可以自己去命名而且使用分隔符:
例如ios,
NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
//分界线 --AaB03x
NSString *MPboundary=[[NSString alloc]initWithFormat:@"--%@",TWITTERFON_FORM_BOUNDARY];
//结束符 AaB03x--
NSString *endMPboundary=[[NSString alloc]initWithFormat:@"%@--",MPboundary];
NSArray *keys= [params allKeys];
for(int i=0;i<[keys count];i++){
//得到当前key
NSString *key=[keys objectAtIndex:i];
//添加分界线,换行
[body appendFormat:@"%@\r\n",MPboundary];
//添加字段名称,换2行
[body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
//添加字段的值
[body appendFormat:@"%@\r\n",[params objectForKey:key]];
}
在循环的最后一行添加完字段的值之后,必须加入\r\n去换行,这是http协议的规则,否则后台接受不到数据。
完了就是图片等文件的上传,如果要字符串和文件上传一起的话,那么首先将文字,也就是上边代码中的string类型的body转换成2进制的,添加到httpbody中,然后将图片添加到httpbody中。
然后是图片上传:
图片上传和html中form表单上传是同一个原理,都需要指定图片的key值,图片上传的模式:form-data。不同的是,图片的名字在浏览器中上传的时候是可以获取的,但是在app中获取不到名字,所以还需要传递名字进去。如果app不想传递图片名的话,那么可以自己随机几个名字,因为后台接受的是data数据,也就是2进制流。
格式:
1,分隔符,
2,声明图片的处理机制,也就是告诉http,用什么机制去处理我这次请求,如果我要上传附件的话,那么就得确定data,所以,声明处理机制:Content-Disposition: form-data; name=\”%@\”; filename=\”%@\”\r\n”
里边包含了图片的key值,key值就是后台接受图片的键值,filename是图片名,content-Disposition是处理机制,每个分号;之后必须得有一个空格,
3,当前附件的类型:Content-Type: %@\r\n\r\n”,@”image/png
然后将所有声明的格式打包成二进制,添加到httpbody中,最后将图片data,也就是图片的二进制流添加到httpbody中。最后,再添加一个回车,也就是\r\n,\r\n也得打包成二进制文件。最后再添加 “\r\n结束符”。
这只是一张图片,也可以说是一个附件,因为没一个附件都得有分隔符,机制,类型,还得有最后的一个回车。
例如:
NSMutableData *RequestData=[[NSMutableData alloc]init];
//将params添加进去
[RequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
for (int i = 0; i < [images count]; i++) {
//
NSMutableString *subBody = [[NSMutableString alloc]init];
//fileName
NSString *fileName = [NSString stringWithFormat:@"%@.png",[self sha1WithSource:[@(i) stringValue]]];
////添加分界线,换行
[subBody appendFormat:@"%@\r\n",MPboundary];
//声明pic字段,文件名
[subBody appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",formNames[i],fileName];
if(_mimeType != nil){
[subBody appendFormat:@"Content-Type: %@\r\n\r\n",_mimeType];
}else{
//声明上传文件的格式
[subBody appendFormat:@"Content-Type: %@\r\n\r\n",@"image/png"];
}
//将body字符串转化为UTF8格式的二进制
[RequestData appendData:[subBody dataUsingEncoding:NSUTF8StringEncoding]];
//将image的data加入
[RequestData appendData:images[i]];
//必加的分隔符,每一个data后边,http的规则
[RequestData appendData:[ @"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
/*===现在httpbody中运行的结果是这样的:====*/
--AaB03x
Content-Disposition: form-data; name="key1"
value1
--AaB03x
Content-Disposition: form-data; name="key3"
value3
--AaB03x
Content-Disposition: form-data; name="key2"
value2
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="b6589fc6ab0dc82cf12099d1c2d40ab994e8410c.png"
Content-Type: image/png
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="356a192b7913b04c54574d18c28d46e6395428ab.png"
Content-Type: image/png
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="da4b9237bacccdf19c0760cab7aec4a8359010b0.png"
Content-Type: image/png
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="77de68daecd823babbb58edb1c8e14d7106e83bb.png"
Content-Type: image/png
--AaB03x--
//结束了,看见这边三个回车,然后又添加了一个结束符,说明上边两个回车,最后结束符还得带一个回车
服务器可以检查当前的各种规则,例如:
2016-03-12 13:55:20.718 HttpDemo[3019:168928] {
"CONTENT_LENGTH" = 1678187;
"CONTENT_TYPE" = "multipart/form-data; boundary=AaB03x";
"CONTEXT_DOCUMENT_ROOT" = "/Applications/XAMPP/xamppfiles/htdocs";
"CONTEXT_PREFIX" = "";
"DOCUMENT_ROOT" = "/Applications/XAMPP/xamppfiles/htdocs";
"DYLD_LIBRARY_PATH" = "/Applications/XAMPP/xamppfiles/lib:/Applications/XAMPP/xamppfiles/lib";
"GATEWAY_INTERFACE" = "CGI/1.1";
"HTTP_ACCEPT" = "*/*";
"HTTP_ACCEPT_ENCODING" = "gzip, deflate";
"HTTP_ACCEPT_LANGUAGE" = "zh-cn";
"HTTP_CONNECTION" = "keep-alive";
"HTTP_HOST" = localhost;
"HTTP_KEY1" = value1;
"HTTP_KEY2" = value2;
"HTTP_KEY3" = value3;
"HTTP_USER_AGENT" = "HttpDemo/1 CFNetwork/758.2.8 Darwin/14.5.0";
PATH = "/usr/bin:/bin:/usr/sbin:/sbin";
"PHP_SELF" = "/php/posttest.php";
"QUERY_STRING" = "";
"REMOTE_ADDR" = "::1";
"REMOTE_PORT" = 58838;
"REQUEST_METHOD" = POST;
"REQUEST_SCHEME" = http;
"REQUEST_TIME" = 1457762120;
"REQUEST_TIME_FLOAT" = "1457762120.683";
"REQUEST_URI" = "/php/posttest.php";
"SCRIPT_FILENAME" = "/Applications/XAMPP/xamppfiles/htdocs/php/posttest.php";
"SCRIPT_NAME" = "/php/posttest.php";
"SERVER_ADDR" = "::1";
"SERVER_ADMIN" = "you@example.com";
"SERVER_NAME" = localhost;
"SERVER_PORT" = 80;
"SERVER_PROTOCOL" = "HTTP/1.1";
"SERVER_SIGNATURE" = "";
"SERVER_SOFTWARE" = "Apache/2.4.10 (Unix) OpenSSL/1.0.1j PHP/5.6.3 mod_perl/2.0.8-dev Perl/v5.16.3";
"UNIQUE_ID" = "VuOvSH8AAAEAAAufp-oAAAAB";
}
最后,在请求的时候,设置http此次请求的body的大小,得再头信息里边设置请求的类型,这样才能和请求一一对应,http协议才能解析这次请求,最后分隔符http是怎么解析的,也是从头信息里边申明了,然后进行body的解析,例如:
NSString *content=[[NSString alloc]initWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY];
//设置HTTPHeader
[_request setValue:content forHTTPHeaderField:@"Content-Type"];
//设置Content-Length
[_request setValue:[NSString stringWithFormat:@"%ld", [RequestData length]] forHTTPHeaderField:@"Content-Length"];
最后设置一下requestHttpBody就行。 这就是一个完整的post请求。 PS,写了一个类似AFNet的库,比较小巧,但是功能指定没人那个强大,但是日常请求,上传是没问题了,性能就不知道了,但是封装苹果的sdk,相信苹果写的性能应该不会那么差吧。 下载:http://pan.baidu.com/s/1o6RCwOi 用法:
#import “AshesHttpRequest.h”
//get,post,post图片上传请求:
-(void)getRequestWithURL:(NSString *)url atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
-(void)postRequestWithURL:(NSString *)url atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
-(void)postRequestWithURL:(NSString *)url atImages:(NSArray *)images atFormNames:(NSArray *)formNames atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
//图片上传注意的是,images里边传递是图片的data数组,names传递每个图片对应的键。一一对应,两个数组count必须相等
GET:
AshesHttpRequest *r = [AshesHttpRequest manager];
[r getRequestWithURL:@"http://skyzizhu2.sinaapp.com/api.php" atParames:nil atWillRequest:^{
NSLog(@"will begin reques!");
} atSucceedRequest:^(AshesHttpRequest *ashesRequest, id reponseObject) {
NSArray *dataArray = (NSArray *)[reponseObject objectForKey:@"data"];
for (int i = 0; i < [dataArray count]; i++) {
AshesHttpModule *model = [[AshesHttpModule alloc]init];
model.title = [NSString stringWithFormat:@"%@",[dataArray[i] objectForKey:@"infoname"]];
model.subTitle = [[NSString stringWithFormat:@"%@",[dataArray[i] objectForKey:@"info_intro"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"title - - -%@,subtitle- - %@",model.title,model.subTitle);
[_dataSource addObject:model];
}
[_tableView reloadData];
} atErrorRequest:^(NSError *error) {
NSLog(@"error - -- %@",error);
}];
//POST多图片上传,如果一张图片则数组里边一个元素即可
NSData *imgData1 = UIImagePNGRepresentation([UIImage imageNamed:@"1.png"]);
NSData *imgData2 = UIImageJPEGRepresentation([UIImage imageNamed:@"User2.jpg"],1);
NSData *imgData3 = UIImageJPEGRepresentation([UIImage imageNamed:@"User1.jpg"],1);
NSData *imgData4 = UIImageJPEGRepresentation([UIImage imageNamed:@"USA.jpg"],1);
NSArray *images = @[imgData1,imgData2,imgData3,imgData4];
NSArray *names = @[@"uploadImage[]",@"uploadImage[]",@"uploadImage[]",@"uploadImage[]"];
NSDictionary *dic = @{@"key1":@"value1",@"key2":@"value2",@"key3":@"value3"};
NSString *cURL = @"http://localhost/php/posttest.php";
//@"http://skyzizhu2.sinaapp.com/api.php"
AshesHttpRequest *manager = [AshesHttpRequest manager];
[manager addHeaderFieldWithFieldValue:dic];
[manager setMimeType:@"image/png"];
[manager postRequestWithURL:cURL atImages:images atFormNames:names atParames:dic atWillRequest:^{
NSLog(@"will request");
} atSucceedRequest:^(AshesHttpRequest *request, id reponseObject) {
NSLog(@"%@",request.request.allHTTPHeaderFields);
NSLog(@"%@ , responseData%@",reponseObject,request.responseData);
} atErrorRequest:^(NSError *error) {
NSLog(@"%@",error);
}];
//过几天提供一个多图上传的后台测试接口
测试接口:
http://skyzizhu2.sinaapp.com/App/posttest.php
返回例子
{
clientIP = "xxx.xxx.99.221";
"client_AGENT" = "HttpDemo/1 CFNetwork/758.2.8 Darwin/14.5.0";
data = {
image1 = "http://skyzizhu2-testappimage.stor.sinaapp.com/b6589fc6ab0dc82cf12099d1c2d40ab994e8410c.png";
image2 = "http://skyzizhu2-testappimage.stor.sinaapp.com/356a192b7913b04c54574d18c28d46e6395428ab.png";
image3 = "http://skyzizhu2-testappimage.stor.sinaapp.com/da4b9237bacccdf19c0760cab7aec4a8359010b0.png";
image4 = "http://skyzizhu2-testappimage.stor.sinaapp.com/77de68daecd823babbb58edb1c8e14d7106e83bb.png";
};
gets = (
);
isUploadSucceed = 1;
posts = {
key1 = value1;
key2 = value2;
key3 = value3;
};
requestTime = 1457766301;
}
鸣谢:http://m.open-open.com/m/code/view/1421912619062
<?php
$finderName = "testSaveFinders/";
if(!is_dir($finderName)){
mkdir($finderName);
}
$keys = array();
$serverPath = dirname('http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]);
foreach ($_FILES as $key => $value) {
# code...
move_uploaded_file($_FILES[$key]["tmp_name"],$finderName . $_FILES[$key]["name"]);
$keys[$key] =$serverPath.'/'.$finderName . $_FILES[$key]["name"];
}
$arrayData = array();
if(count($keys) > 0){
$arrayData['isSucceed'] = TRUE;
}else{
$arrayData['isSucceed'] = FALSE;
}
$arrayData['requestTime'] = $_SERVER['REQUEST_TIME'];
$arrayData['clientIP'] = $_SERVER["REMOTE_ADDR"];
$arrayData['client_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
$arrayData['data'] = $keys;
echo json_encode($arrayData);
?>

