C++上传文件到Restful Web Service服务端——客户端篇
转载:http://blog.csdn.net/ronux/article/details/8244840
由于项目的需要,要做一个C++的http请求客户端实现上传数据到服务端的功能,服务端用的是Spring MVC实现的Restful Web Service,起初设计时在服务端以byte[]的形式接受数据,毕竟服务端用java写的,把接收到的数据流解析成相应的文件这是完全可行的。
如果写过Java或者其他语言的Http请求的人都知道,请求方式可以分GET和POST两种,不过GET传递的数据充其量只是简单类型的参数而已,在请求时的数据格式如代码,这是请求http://www.ip138.com:8080/search.asp?mobile=1565888&action=mobile站点的请求参数:
GET /search.asp?mobile=1565888&action=mobile HTTP/1.1 Host: www.ip138.com:8080 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive
以上是用FireFox的插件Live Http Headers获取的数据,而且这个插件可以自行编辑参数定制HTTP请求,方便编程的测试工作,具体使用可以查阅资料。这里面好多参数并不是必须的,但是为了模拟浏览器的效果最好设置一下。另外对于GET请求不需要设置Content-Type和Content-Length参数,通常这两个也是成对出现的。User-Agent表示请求客户端的浏览器和OS信息,这里模拟浏览器访问可以自己定制。
POST请求的数据是放在请求体里的,具体信息可参考http://blog.csdn.net/yc0188/article/details/4741871,严格按照要求组织请求数据即可。注意请求头和请求数据之间的空行。这是一个Web系统的POST请求数据:
POST /myproject/infoUpdate HTTP/1.1 Host: 192.168.52.250:8088 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive Referer: http://192.168.52.250:8088/myproject/infoUpdate.action Content-Type: application/x-www-form-urlencoded Content-Length: 24 infoId=3¤tPage=1
另外在具体组织请求信息时,Windows下是用\r\n作为换行符的,但是在linux下这样用也不错,虽然linux的换行符是\n。
strcat(requestStr, "POST "); strcat(requestStr, api); strcat(requestStr, " HTTP/1.1\r\n"); strcat(requestStr, "Accept: application/x-ms-application, image/gif, " "application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, " "application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n"); strcat(requestStr, "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:17.0) Gecko/17.0 Firefox/17.0\r\n"); strcat(requestStr, "Accept-Language: zh-CN,en-US;q=0.5\r\n"); strcat(requestStr, "Accept-Encoding: gzip, deflate\r\n"); strcat(requestStr, "Host: "); strcat(requestStr, hostname); strcat(requestStr, "\r\n");
POST简单的数据以上就可以做到,但是对于上传文件呢。文件通常是二进制形式的,也就是要传输流。不过我们对struts2或者Spring MVC的上传文件表单不陌生,于是我监测了一个上传文件操作的请求数据:
POST /myproject/infoUpdate HTTP/1.1 Host: 192.168.52.250:8088 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------41184676334 Content-Length: 95254 -----------------------------41184676334\r\n Content-Disposition: form-data; name="id"\r\n \r\n 6\r\n -----------------------------41184676334\r\n Content-Disposition: form-data; name="doc"; filename="faq_info.jpg"\r\n Content-Type: image/jpeg\r\n \r\n ……请求的数据流…… \r\n-----------------------------41184676334--\r\n
这里用到了boundary(分隔符),用来间隔不同的请求数据,这是Content-Type类型为multipart/form-data(Spring MVC Restful Web Service也支持multipart/mixed的类型用于上传文件)时所要求的数据格式,和Content-Type为application/x-www-form-urlencoded时用&间隔参数是一样的。但是要注意使用的规则,可参见http://blog.csdn.net/mspinyin/article/details/6141638。Java版本的HttpURLConnection请求可参考:http://lapulande.iteye.com/blog/719581。C++版的可以用Windows下的winsock,代码可参考:http://www.cnblogs.com/evlon/archive/2007/08/13/853145.html和http://www.oschina.net/code/snippet_176076_5908分别是windows下的和linux的范例,上述链接代码只是使用socket完成了请求操作,并没有做上传数据的部分。我建议用到的朋友可以先用Java版的客户端进行上传文件测试,如果测试通过了,可以比照着写C++的代码,在请求头部分C++老老实实地组织对应的参数就可以,请求数据部分要严格注意换行的地方,以及上传文件的长度加入Content-Length(包括上传数据长度和分隔符等其他字符的长度)。
文件流的长度可以这样获得:
ifstream fin(filePath ,ios::in|ios::binary); if(!fin){ cout<<"File open error!\n"; return -1; } //---get the length of the file int temp = fin.tellg(); fin.seekg(0,ios_base::end); int len = fin.tellg(); fin.seekg(temp);
获得长度后重新定位到文件开始处。
上传文件流时要读取多少send多少,否则服务端得到的是错误的流,且在send时不要用strlen(c)来指定发送的长度,因为char* c中可能包含‘\0’这样的空字符,从而使用了错误的长度。
char c[1024]; memset(c, 0,1024); int tmpLen=0; int i=0; while(!fin.eof()) { if(tmpLen<(len/1024)*1024) //首先按照1024的单位传 { fin.read(c, 1024); send(sock,c,1024,0); Sleep(1); //休息,为了降低CPU使用率以及照顾带宽 tmpLen+=1024; }else //如果剩余的小于等于1024了,单独send剩余的字节 { fin.read(c,len-(len/1024)*1024); send(sock,c,len-(len/1024)*1024,0); break; } } fin.close();
在C++里用socket建立的请求,没有使用HTTP协议,可以选择TCP或者UDP协议,默认用TCP。我在测试中使用WireShark监测传输的数据时,在HTTP相关协议中没有看到数据,原因就在于此,Java版请求的就可以检测到。
上一篇: python基础学习(二十一)Python异常处理
下一篇: JDBC加载Driver