如何使用multipart/form-data格式上传文件

如何使用multipart/form-data格式上传文件


------------------------------------------------------------------
大家好,我是许飞,微软拼音的开发实习生。在网络编程中,经常用到从服务器上“下载”一些数据,有时却要向服务器“上传”数据。曾在一个原型中使用了“multipart/form-data”格式向Web服务器上传文件,这里和大家分享。
------------------------------------------------------------------ 
有时,在网络编程过程中需要向服务器上传文件。Multipart/form-data是上传文件的一种方式。

Multipart/form-data其实就是浏览器用表单上传文件的方式。最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器。

如何使用multipart/form-data格式上传文件_第1张图片

表单形式上传附件

具体的步骤是怎样的呢?

首先,客户端和服务器建立连接(TCP协议)。

第二,客户端可以向服务器端发送数据。因为上传文件实质上也是向服务器端发送请求。

第三,客户端按照符合“multipart/form-data”的格式向服务器端发送数据。

Multipart/form-data的格式是怎样的呢?

既然Multipart/form-data格式就是浏览器用表单提交数据的格式,我们就来看看文件经过浏览器编码后是什么样子。

如何使用multipart/form-data格式上传文件_第2张图片

HTML表单

浏览器打开的表单

 

点击“Browse…”分别选择“unknow.gif”和“unknow1.gif”文件,点击“submit”按纽后,文件将被上传到服务器。

下面是服务器收到的数据:

如何使用multipart/form-data格式上传文件_第3张图片

服务器收到的数据

这是一个POST请求。所以数据是放在请求体内,而不是请求头内。

这行指出这个请求是“multipart/form-data”格式的,且“boundary”是 “---------------------------7db15a14291cce”这个字符串。

不难想象,“boundary”是用来隔开表单中不同部分数据的。例子中的表单就有 2 部分数据,用“boundary”隔开。“boundary”一般由系统随机产生,但也可以简单的用“-------------”来代替。

实际上,每部分数据的开头都是由"--" + boundary开始,而不是由 boundary 开始。仔细看才能发现下面的开头这段字符串实际上要比 boundary 多了个 “--”

clip_image011

紧接着 boundary 的是该部分数据的描述。

接下来才是数据。

“GIF”gif格式图片的文件头,可见,unknow1.gif确实是gif格式图片。

在请求的最后,则是 "--" + boundary + "--" 表明表单的结束

 

需要注意的是,在html协议中,用 “/r/n” 换行,而不是 “/n”

下面的代码片断演示如何构造multipart/form-data格式数据,并上传图片到服务器。

[c-sharp]  view plain copy
  1. //---------------------------------------  
  2.   
  3. // this is the demo code of using multipart/form-data to upload text and photos.  
  4.   
  5. // -use WinInet APIs.  
  6.   
  7. //  
  8.   
  9. //  
  10.   
  11. // connection handlers.  
  12.   
  13. //  
  14.   
  15. HRESULT hr;  
  16.   
  17. HINTERNET m_hOpen;   
  18.   
  19. HINTERNET m_hConnect;  
  20.   
  21. HINTERNET m_hRequest;  
  22.   
  23. //  
  24.   
  25. // make connection.  
  26.   
  27. //  
  28.   
  29. ...  
  30.   
  31. //  
  32.   
  33. // form the content.  
  34.   
  35. //  
  36.   
  37. std::wstring strBoundary = std::wstring(L"------------------");  
  38.   
  39. std::wstring wstrHeader(L"Content-Type: multipart/form-data, boundary=");  
  40.   
  41. wstrHeader += strBoundary;  
  42.   
  43. HttpAddRequestHeaders(m_hRequest, wstrHeader.c_str(), DWORD(wstrHeader.size()), HTTP_ADDREQ_FLAG_ADD);  
  44.   
  45. //  
  46.   
  47. // "std::wstring strPhotoPath" is the name of photo to upload.  
  48.   
  49. //  
  50.   
  51. //  
  52.   
  53. // uploaded photo form-part begin.  
  54.   
  55. //  
  56.   
  57. std::wstring strMultipartFirst(L"--");  
  58.   
  59. strMultipartFirst += strBoundary;  
  60.   
  61. strMultipartFirst += L"/r/nContent-Disposition: form-data; name=/"pic/"; filename=";  
  62.   
  63. strMultipartFirst += L"/"" + strPhotoPath + L"/"";  
  64.   
  65. strMultipartFirst += L"/r/nContent-Type: image/jpeg/r/n/r/n";  
  66.   
  67. //  
  68.   
  69. // "std::wstring strTextContent" is the text to uploaded.   
  70.   
  71. //  
  72.   
  73. //  
  74.   
  75. // uploaded text form-part begin.  
  76.   
  77. //  
  78.   
  79. std::wstring strMultipartInter(L"/r/n--");  
  80.   
  81. strMultipartInter += strBoundary;  
  82.   
  83. strMultipartInter += L"/r/nContent-Disposition: form-data; name=/"status/"/r/n/r/n";  
  84.   
  85. std::wstring wstrPostDataUrlEncode(CEncodeTool::Encode_Url(strTextContent));  
  86.   
  87. // add text content to send.  
  88.   
  89. strMultipartInter += wstrPostDataUrlEncode;  
  90.   
  91. std::wstring strMultipartEnd(L"/r/n--");  
  92.   
  93. strMultipartEnd += strBoundary;  
  94.   
  95. strMultipartEnd += L"--/r/n";  
  96.   
  97. //  
  98.   
  99. // open photo file.  
  100.   
  101. //  
  102.   
  103. // ws2s(std::wstring)  
  104.   
  105. // -transform "strPhotopath" from unicode to ansi.  
  106.   
  107. std::ifstream *pstdofsPicInput = new std::ifstream;  
  108.   
  109. pstdofsPicInput->open((ws2s(strPhotoPath)).c_str(), std::ios::binary|std::ios::in);  
  110.   
  111. pstdofsPicInput->seekg(0, std::ios::end);  
  112.   
  113. int nFileSize = pstdofsPicInput->tellg();  
  114.   
  115. if(nPicFileLen == 0)  
  116.   
  117. {  
  118.   
  119. return E_ACCESSDENIED;  
  120.   
  121. }  
  122.   
  123. char *pchPicFileBuf = NULL;  
  124.   
  125. try  
  126.   
  127. {  
  128.   
  129. pchPicFileBuf = new char[nPicFileLen];  
  130.   
  131. }  
  132.   
  133. catch(std::bad_alloc)  
  134.   
  135. {  
  136.   
  137. hr = E_FAIL;  
  138.   
  139. }  
  140.   
  141. if(FAILED(hr))  
  142.   
  143. {  
  144.   
  145. return hr;  
  146.   
  147. }  
  148.   
  149. pstdofsPicInput->seekg(0, std::ios::beg);  
  150.   
  151. pstdofsPicInput->read(pchPicFileBuf, nPicFileLen);  
  152.   
  153. if(pstdofsPicInput->bad())  
  154.   
  155. {  
  156.   
  157. pstdofsPicInput->close();  
  158.   
  159. hr = E_FAIL;  
  160.   
  161. }  
  162.   
  163. delete pstdofsPicInput;  
  164.   
  165. if(FAILED(hr))  
  166.   
  167. {  
  168.   
  169. return hr;  
  170.   
  171. }  
  172.   
  173. // Calculate the length of data to send.  
  174.   
  175. std::string straMultipartFirst = CEncodeTool::ws2s(strMultipartFirst);  
  176.   
  177. std::string straMultipartInter = CEncodeTool::ws2s(strMultipartInter);  
  178.   
  179. std::string straMultipartEnd = CEncodeTool::ws2s(strMultipartEnd);  
  180.   
  181. int cSendBufLen = straMultipartFirst.size() + nPicFileLen + straMultipartInter.size() + straMultipartEnd.size();  
  182.   
  183. // Allocate the buffer to temporary store the data to send.  
  184.   
  185. PCHAR pchSendBuf = new CHAR[cSendBufLen];  
  186.   
  187. memcpy(pchSendBuf, straMultipartFirst.c_str(), straMultipartFirst.size());  
  188.   
  189. memcpy(pchSendBuf + straMultipartFirst.size(), (const char *)pchPicFileBuf, nPicFileLen);  
  190.   
  191. memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen, straMultipartInter.c_str(), straMultipartInter.size());  
  192.   
  193. memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen + straMultipartInter.size(), straMultipartEnd.c_str(), straMultipartEnd.size());  
  194.   
  195. //  
  196.   
  197. // send the request data.  
  198.   
  199. //  
  200.   
  201. HttpSendRequest(m_hRequest, NULL, 0, (LPVOID)pchSendBuf, cSendBufLen)  
  202.   
  203. ...  


你可能感兴趣的:(如何使用multipart/form-data格式上传文件)