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); ?>