C++上傳文件到Restful Web Service服務端——客戶端篇

因爲項目的須要,要作一個C++的http請求客戶端實現上傳數據到服務端的功能,服務端用的是Spring MVC實現的Restful Web Service,起初設計時在服務端以byte[]的形式接受數據,畢竟服務端用java寫的,把接收到的數據流解析成相應的文件這是徹底可行的。html

    若是寫過Java或者其餘語言的Http請求的人都知道,請求方式能夠分GET和POST兩種,不過GET傳遞的數據充其量只是簡單類型的參數而已,在請求時的數據格式如代碼,這是請求http://www.ip138.com:8080/search.asp?mobile=1565888&action=mobile站點的請求參數:java

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信息,這裏模擬瀏覽器訪問能夠本身定製。linux

    POST請求的數據是放在請求體裏的,具體信息可參考http://blog.csdn.net/yc0188/article/details/4741871,嚴格按照要求組織請求數據便可。注意請求頭和請求數據之間的空行。這是一個Web系統的POST請求數據:ios

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的上傳文件表單不陌生,因而我監測了一個上傳文件操做的請求數據:windows

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(包括上傳數據長度和分隔符等其餘字符的長度)。api

    文件流的長度能夠這樣得到:瀏覽器

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

得到長度後從新定位到文件開始處。app

    上傳文件流時要讀取多少send多少,不然服務端獲得的是錯誤的流,且在send時不要用strlen(c)來指定發送的長度,由於char* c中可能包含‘\0’這樣的空字符,從而使用了錯誤的長度。socket

  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版請求的就能夠檢測到。

相關文章
相關標籤/搜索