您的位置:首页 > 其它

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

2015-10-30 16:03 821 查看
有时,在网络编程过程中需要向服务器上传文件。Multipart/form-data是上传文件的一种方式。

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



表单形式上传附件

具体的步骤是怎样的呢?

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

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

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

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

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



HTML表单



浏览器打开的表单

 

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

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



服务器收到的数据

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



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

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

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





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



接下来才是数据。



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

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

 

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

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

[c-sharp] view
plaincopy

//---------------------------------------  

  

// this is the demo code of using multipart/form-data to upload text and photos.  

  

// -use WinInet APIs.  

  

//  

  

//  

  

// connection handlers.  

  

//  

  

HRESULT hr;  

  

HINTERNET m_hOpen;   

  

HINTERNET m_hConnect;  

  

HINTERNET m_hRequest;  

  

//  

  

// make connection.  

  

//  

  

...  

  

//  

  

// form the content.  

  

//  

  

std::wstring strBoundary = std::wstring(L"------------------");  

  

std::wstring wstrHeader(L"Content-Type: multipart/form-data, boundary=");  

  

wstrHeader += strBoundary;  

  

HttpAddRequestHeaders(m_hRequest, wstrHeader.c_str(), DWORD(wstrHeader.size()), HTTP_ADDREQ_FLAG_ADD);  

  

//  

  

// "std::wstring strPhotoPath" is the name of photo to upload.  

  

//  

  

//  

  

// uploaded photo form-part begin.  

  

//  

  

std::wstring strMultipartFirst(L"--");  

  

strMultipartFirst += strBoundary;  

  

strMultipartFirst += L"/r/nContent-Disposition: form-data; name=/"pic/"; filename=";  

  

strMultipartFirst += L"/"" + strPhotoPath + L"/"";  

  

strMultipartFirst += L"/r/nContent-Type: image/jpeg/r/n/r/n";  

  

//  

  

// "std::wstring strTextContent" is the text to uploaded.   

  

//  

  

//  

  

// uploaded text form-part begin.  

  

//  

  

std::wstring strMultipartInter(L"/r/n--");  

  

strMultipartInter += strBoundary;  

  

strMultipartInter += L"/r/nContent-Disposition: form-data; name=/"status/"/r/n/r/n";  

  

std::wstring wstrPostDataUrlEncode(CEncodeTool::Encode_Url(strTextContent));  

  

// add text content to send.  

  

strMultipartInter += wstrPostDataUrlEncode;  

  

std::wstring strMultipartEnd(L"/r/n--");  

  

strMultipartEnd += strBoundary;  

  

strMultipartEnd += L"--/r/n";  

  

//  

  

// open photo file.  

  

//  

  

// ws2s(std::wstring)  

  

// -transform "strPhotopath" from unicode to ansi.  

  

std::ifstream *pstdofsPicInput = new std::ifstream;  

  

pstdofsPicInput->open((ws2s(strPhotoPath)).c_str(), std::ios::binary|std::ios::in);  

  

pstdofsPicInput->seekg(0, std::ios::end);  

  

int nFileSize = pstdofsPicInput->tellg();  

  

if(nPicFileLen == 0)  

  

{  

  

return E_ACCESSDENIED;  

  

}  

  

char *pchPicFileBuf = NULL;  

  

try  

  

{  

  

pchPicFileBuf = new char[nPicFileLen];  

  

}  

  

catch(std::bad_alloc)  

  

{  

  

hr = E_FAIL;  

  

}  

  

if(FAILED(hr))  

  

{  

  

return hr;  

  

}  

  

pstdofsPicInput->seekg(0, std::ios::beg);  

  

pstdofsPicInput->read(pchPicFileBuf, nPicFileLen);  

  

if(pstdofsPicInput->bad())  

  

{  

  

pstdofsPicInput->close();  

  

hr = E_FAIL;  

  

}  

  

delete pstdofsPicInput;  

  

if(FAILED(hr))  

  

{  

  

return hr;  

  

}  

  

// Calculate the length of data to send.  

  

std::string straMultipartFirst = CEncodeTool::ws2s(strMultipartFirst);  

  

std::string straMultipartInter = CEncodeTool::ws2s(strMultipartInter);  

  

std::string straMultipartEnd = CEncodeTool::ws2s(strMultipartEnd);  

  

int cSendBufLen = straMultipartFirst.size() + nPicFileLen + straMultipartInter.size() + straMultipartEnd.size();  

  

// Allocate the buffer to temporary store the data to send.  

  

PCHAR pchSendBuf = new CHAR[cSendBufLen];  

  

memcpy(pchSendBuf, straMultipartFirst.c_str(), straMultipartFirst.size());  

  

memcpy(pchSendBuf + straMultipartFirst.size(), (const char *)pchPicFileBuf, nPicFileLen);  

  

memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen, straMultipartInter.c_str(), straMultipartInter.size());  

  

memcpy(pchSendBuf + straMultipartFirst.size() + nPicFileLen + straMultipartInter.size(), straMultipartEnd.c_str(), straMultipartEnd.size());  

  

//  

  

// send the request data.  

  

//  

  

HttpSendRequest(m_hRequest, NULL, 0, (LPVOID)pchSendBuf, cSendBufLen)  

  

...  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: