笔谈HTTP Multipart POST请求上传文件
公司一做iOS开发的同事用HTTP Multipart POST请求上传语音数据,但是做了两天都没搞定,项目经理找到我去帮忙弄下。以前做项目只用过get、post,对于现在这个跟服务器交互的表单请求我没有做过,但是程序员学习能力还是要有的,解决问题的方法和经验是很重要的。做过2000万用户量的业务sdk的开发,这点东西自然不在话下,优秀的程序员就是要有解决问题的能力与方法。
1) 接口地址
接口地址为:http://ip:port/upload
2) UploadRequest消息定义
Form表单参数定义如下
固定name属性为
参数名 |
参数类型 |
是否必填 |
描述 |
fileData |
字符串变长 |
Y |
标识为文件数据
其中的filename属性为 原始文件名
注意:该名称需要由上传端确保唯一性,此唯一只需要保证同一设备ID的情况下不重复即可 |
DevIDs |
字符串变长 |
Y |
设备ID(多个设备) |
Transport |
字符串变长 |
Y |
音频编码信息,只支持G711A |
ClientID |
字符串变长 |
Y |
客户端ID |
3) UploadResponse消息定义
无扩展消息
举例
1) uploadRequest消息
POST /spi/mediaul.do HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Content-Type: multipart/form-data; boundary=7da29f2d890386
Host: abc.com
Content-Length: 1516663(文件的实际总大小)
Connection: Keep-Alive
Cache-Control: no-cache
--7da29f2d890386
Content-Disposition: form-data; name="DevIDs";
660000000000000062349002a94658da, 66000000000000005550c42f9000e028
--7da29f2d890386
Content-Disposition: form-data; name="Transport";
PCMA/8000/1
--7da29f2d890386
Content-Disposition: form-data; name="ClientID";
XXXXXXX
--7da29f2d890386
Content-Disposition: form-data; name="fileData"; filename="XXX"
Content-Type: application/octet-stream
文件实际内容流数据
--7da29f2d890386--
2) uploadResponse消息
<?xml version="1.0" encoding="utf-8"?>
<Message Version="1.0">
<Header MsgType="UploadResponse" MsgSeq="11"/>
<Result RetCode="0" RetDesc="操作成功"/>
</Message>
实现这个post表单请求,最重要的就是格式了,要知道 "\r\n" 与 "\n" 的区别,什么时候要换两行\r\n\r\n或多行\r\n\r\n\...,什么时候只能换一行\r\n,空格 不能漏掉,任何一个细节出现问题都会导致 the connection lost,这就是考察一个人的仔细程度。
实现这个Multipart POST请求其实很简答的,细节决定成败,对于以前没有写个这种与服务器的表单交互请求的,那就只能在网上多搜集资料,认真比对了。所谓“熟读唐诗三百首,不会做诗也会吟”。我在网上看了一些资料,有的博客完全是误导人,这就要多看多对比,仔细甄别,做事与看人是同一道理,了解一个人不能凭一面之辞妄下结论。
下面来看下我写的实现post与服务器进行表单数据交互的实现代码:
// // Created by dev.temobi on 15/5/7. // Copyright (c) 2015年 dev.temobi. All rights reserved. // #import "ViewController.h" #define Boudary @"7da29f2d890386" //http://192.168.3.177:2800/upload #define iAddrIP @"192.168.3.177" #define iAddrPort 2800 @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self uploadAudioToServiceSelectedDevice:@[@"86000123456884846234123456789007"] delegate:self]; } - (void)uploadAudioToServiceSelectedDevice:(NSArray *)arrDevices delegate:(id)delegate//此delegate为 NSURLConnectionDelegate { NSString *url = [NSString stringWithFormat:@"http://%@:%i/upload",iAddrIP,iAddrPort]; NSMutableData *body = [ NSMutableData data ]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", Boudary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"DevIDs\"\r\n\r\n%@\r\n", arrDevices[0]] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", Boudary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"Transport\"\r\n\r\n%@\r\n", @"PCMA/8000/1"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", Boudary] dataUsingEncoding:NSUTF8StringEncoding]]; NSString *TM_SDK_NULL_STRING = @""; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"ClientID\"\r\n\r\n%@\r\n", TM_SDK_NULL_STRING] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", Boudary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"fileData\"; filename=\"aaaaa.pcma\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; // --注意只有一个\r\n [body appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; // --注意有两个\r\n\r\n NSString *RecordAudioFilePath = [[NSBundle mainBundle] pathForResource:@"aaaaa" ofType:@"pcma"]; NSData *fileData = [NSData dataWithContentsOfFile:RecordAudioFilePath]; // ------语音数据--------- [body appendData:fileData]; [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", Boudary] dataUsingEncoding:NSUTF8StringEncoding]]; // ------注意分隔符Boudary前后各有一个\r\n--------- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]]; [request setTimeoutInterval:0]; [request setValue:@"zh-cn" forHTTPHeaderField:@"Accept-Language"]; [request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)body.length] forHTTPHeaderField:@"Content-Length"]; [request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"]; [request setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", Boudary] forHTTPHeaderField:@"Content-Type"]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:body]; NSError *error = nil; NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; if (data == nil) { NSLog(@"send request failed: %@", error); } [request release]; NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"response: %@", response);
[response release]; }
与服务器交互的响应结果如下:
2015-05-08 10:10:11.834 PostDataToServer[4584:1627837] response: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Message> <Header MsgSeq="0" MsgType="UploadResponse"/> <Result RetCode="400"/> </Message>
就是这么简单,语音数据成功上传给服务器了。
完整工程PostDataToServer 的下载地址为:http://pan.baidu.com/s/1qWkKnta
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。