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&currentPage=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.htmlhttp://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版请求的就可以检测到。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值