其实不应当归类到iOS当中,因为实际上是属于RFC 1867、RFC 1341一类的东西。他们规定:这种需要执行 多部分POST(multipart POST)标准的情形,需要把图片啊视频啊等数据设置到HTTP【正文】当中,也就是要把表单项数据(也就是参数及其值)跟二进制文件数据相结合。
其中最让人望而生畏也最容易搞不懂的怕就是“边界字符串”了,其实就是任意内容的字符串(但是要两个横线开头),只要字符串的内容不与正文的其他部分发生冲突就行啦,比如:
NSString *boundaryStr=[[NSStringalloc]initWithFormat:@"--WriteByShareHoney"];
参见:http://www.ietf.org/rfc/rfc1341.txt
The encapsulation boundary is defined as a line consisting entirely of two hyphen characters ("-", decimal code 45) followed by the boundary parameter value from the Content-Type header field.
【正文】需要由“边界字符串”封装,所以第一行就应该出现boundaryStr,而且之前【必须】要有换行。
这也要在【HTTPHeader】中对应的设置,如下:
//设置HTTPHeader
NSString *content=[[NSString alloc]initWithFormat:@"multipart/form-data;boundary=%@",boundaryStr];
[request setValue:content forHTTPHeaderField:@"Content-Type"];
即:Content-type: multipart/form-data, boundary=AaB03x
这直接指定了http post请求的编码方式为multipart/form-data;
boundary=AaB03x则指AaB03x为“边界字符串”。
参考自http://borissun.iteye.com/blog/1338991
往下就好说了,把不同的字段用“边界字符串”分开:
//“边界字符串 单独占一行
--AaB03x
//字段 field1。 content-disposition 个人理解为 内容特性 的意思。
content-disposition: form-data; name="field1"
//换行2次 空行,必须的
//字段的值 单独占一行
Hello Boris!
//“边界字符串 单独占一行
--AaB03x
//字段 name 和 filename
content-disposition: form-data; name="pic"; filename="boris.png"
//内容类型 与filename字段相对应
Content-Type: image/png
//换行2次 空行,必须的
<89504e47 ... 图片的二进制内容...>
//“边界字符串 单独占一行
--AaB03x
这样看可能还有些蒙,其实就是合并几个字段:
Content-type: multipart/form-data, boundary=...
content-disposition: form-data; name=...; filename=...
Content-Type: ...
参见代码吧:
//分界线的标识符,也就是“边界字符串”
NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_urlStr]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10];
//分界线添加"--"【前缀】,【重要】
NSString *MPboundary=[[NSString alloc]initWithFormat:@"--%@",TWITTERFON_FORM_BOUNDARY];
//结束符就是分界线
NSString *endMPboundary=[[NSString alloc]initWithFormat:@"%@--",MPboundary];
NSData *data = nil;
if (UIImagePNGRepresentation(image)) {
data = UIImagePNGRepresentation(image);
}
else
{
data = UIImageJPEGRepresentation(image, 1.0);
}
//http body的字符串
NSMutableString *body=[[NSMutableString alloc]init];
//遍历参数集合,将键值对合并到body(正文)中去
NSArray *keys= [postParems allKeys];
for(int i=0;i<[keys count];i++)
{
NSString *key=[keys objectAtIndex:i];
//如果key不是pic,说明value是字符类型,比如name:Boris
if(![key isEqualToString:@"pic"])
{
//添加分界线,【换行】
[body appendFormat:@"%@\r\n",MPboundary];
//添加字段名称,【换2行】
[body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
//添加字段的值
[body appendFormat:@"%@\r\n",[postParems objectForKey:key]];
}
}
添加分界线,换行
[body appendFormat:@"%@\r\n",MPboundary];
//声明pic字段,文件名为boris.png
[body appendFormat:@"Content-Disposition: form-data; name=\"uploadedfile\"; filename=\"boris.png\"\r\n"];
//声明上传文件的格式
if (UIImagePNGRepresentation(image))
{
//【换2行】
[body appendFormat:@"Content-Type: image/png\r\n\r\n"];
}
else
{
[body appendFormat:@"Content-Type: image/jpg\r\n\r\n"];
}
//声明结束符,主意有【换行】
NSString *end=[[NSString alloc]initWithFormat:@"\r\n%@",endMPboundary];
//声明myRequestData,用来放入http body
NSMutableData *myRequestData=[NSMutableData data];
//将body字符串转化为【UTF8格式的二进制】
[myRequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
//将image的data加入
[myRequestData appendData:data];
//加入结束符--AaB03x-- 【正文】封装完毕。
[myRequestData appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];
//设置【HTTPHeader】中Content-Type的值
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:@"%d", [myRequestData length]] forHTTPHeaderField:@"Content-Length"];
//设置【http body】
[request setHTTPBody:myRequestData];
//设置【http method】
[request setHTTPMethod:@"POST"];
[NSURLConnection connectionWithRequest:request delegate:self];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
另:
如果上传的文件很大,数10m甚至上100m,建议使用NSInputStream从文件中读取,而不是用NSData从内存中获取数据,不然怕是内存吃紧,从而app也就不稳定了。