MFC使用http post请求上传文件
生活随笔
收集整理的這篇文章主要介紹了
MFC使用http post请求上传文件
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- 代碼
前言
筆者在Windows編程開發時候,有個上傳文件的需求,服務端給的接口是http接口,和網頁上面 表單上傳文件一樣(form-data方式)。當然我們拿到這需求,一想 用Windows原生的API去做 肯定麻煩一點 當然也能做,再一想 我們用支持http協議的框架不就行了嘛,MFC、libcurl、OpenSSL等等應該很多。筆者比較熟悉的2個庫,MFC和libcurl,所以決定用這2個庫來做文件上傳。
和大家一樣,剛開始覺的很簡單,網上搜下然后改下就OK。但是筆者卻花費了些時間才完成。一是太相信postman了被postman給誤導了,二是把代碼寫的更通用更簡潔了一點。
筆者剛開用postman模擬上傳文件,能成功上傳。然后使用postman生成的http報文數據去組裝,按理來說肯定可以的,但是就是被誤導。
代碼
網上貼的代碼,感覺的都不是很好,所以筆者稍微寫整潔了一點,更通用了一點,下面是核心代碼 貼出來。實際上可以根據文件后綴名獲取到該文件的Content-Type的,可以做的更智能點 程序內部加一個數據字典就可以實現。筆者這里就偷懶了。
class PostUpFileValStruct; typedef std::vector<PostUpFileValStruct> PostUpFileValVector;class PostUpFileValStruct { public:CString name; // 普通字段、文件字段 有值CString filename; // 文件字段 有值CString ContentType; // 文件字段 有值CString content; // 普通字段 有值 }; /*http post請求上傳文件函數 */ bool CUtility::HttpPostUploadFile(const PostUpFileValVector &PostParamVec/*in 請求參數列表*/, const CString &strURL/*in 上傳文件服務器接口url*/,std::map<CString,CString> &requestHeaders/*in 請求頭數據*/,CString &strResponse/*out 響應數據*/,CString &strErrMsg/* out 上傳文件出錯信息*/) {bool bResult = false;CString strPostUrl = strURL;DWORD dw(0);CString strServer;CString strObject;INTERNET_PORT nPort;AfxParseURL(strPostUrl, dw, strServer, strObject, nPort );CInternetSession session;if(strURL.IsEmpty()){strErrMsg = _T("strURL為空!!!");return bResult;}const int nTimeOut = 5000;//3000session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, nTimeOut); //重試之間的等待延時session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 3); //重試次數session.SetOption(INTERNET_OPTION_SEND_TIMEOUT, nTimeOut );session.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT, nTimeOut );CHttpConnection *pHttpConnection(NULL);CHttpFile *pHttpFile(NULL);try{pHttpConnection = session.GetHttpConnection( strServer, INTERNET_FLAG_DONT_CACHE , nPort );CString str = L"https:";CString strTemp = strURL.Left(str.GetLength());BOOL bHttpsFlag(FALSE);if (0 == strTemp.CompareNoCase(str)){bHttpsFlag = TRUE;}if (bHttpsFlag){pHttpFile = pHttpConnection->OpenRequest( CHttpConnection::HTTP_VERB_POST, strObject, NULL, 0, 0, _T("HTTP/1.1"), INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD|INTERNET_FLAG_SECURE);}else{pHttpFile = pHttpConnection->OpenRequest( CHttpConnection::HTTP_VERB_POST, strObject, NULL, 0, 0, _T("HTTP/1.1"), INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD);}//CString cstrBoundary = _T("----WebKitFormBoundaryoDL1nQAJxdvsAlcu");CString cstrBoundary = _T("----") + CUtility::GetGUID(); // 這個字符串,隨意生成即可,主要是界定字段數據的。CString boundaryHead;boundaryHead.Format(_T("Content-Type:multipart/form-data; boundary=%s"),cstrBoundary);pHttpFile->AddRequestHeaders(_T("Accept: application/json, text/plain, */*" ) );pHttpFile->AddRequestHeaders(_T("Accept-Encoding: gzip, deflate, br") );pHttpFile->AddRequestHeaders(_T("Accept-Language: zh-cn"));//pHttpFile->AddRequestHeaders(_T("Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryoDL1nQAJxdvsAlcu"));pHttpFile->AddRequestHeaders(_T("Cache-Control: no-cache"));std::map<CString,CString>::iterator it = requestHeaders.begin();CString strHeadTemp;for(; it != requestHeaders.end(); it++){strHeadTemp.Format(_T("%s: %s"),it->first,it->second);pHttpFile->AddRequestHeaders(strHeadTemp);}pHttpFile->AddRequestHeaders(boundaryHead);if (bHttpsFlag){DWORD dwFlags(0);pHttpFile->QueryOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlags);dwFlags |= (SECURITY_FLAG_IGNORE_CERT_CN_INVALID |SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |SECURITY_FLAG_IGNORE_WRONG_USAGE |SECURITY_FLAG_IGNORE_REVOCATION |SECURITY_FLAG_IGNORE_UNKNOWN_CA);pHttpFile->SetOption(INTERNET_OPTION_SECURITY_FLAGS, dwFlags);}if( pHttpFile != NULL ){// MFC在傳輸數據前要先發送數據長度,所以先計算上傳報文數據的長度--- 通過post請求上傳文件報文格式如下 ---///一個普通的字段//\r\n--{strBoundary}\r\nContent-Disposition: form-data; name="tranCode"\r\n\r\n2007//一個文件的字段//\r\n--{strBoundary}\r\nContent-Disposition: form-data; name="file"; filename="1590721092834.jpg"\r\nContent-Type: image/jpeg\r\n\r\n{二進制數據}//報文結束標志//\r\n--{strBoundary}--\r\n// 結束部分報文std::string strBoundary = CUtility::W2Astring(cstrBoundary);std::string dataEnd = "\r\n--"+strBoundary+"--\r\n";// 傳輸數據時是以字節為單位進行傳輸的,所以這里都用std::string或者char*進行處理// 1.計算上傳報文數據長度DWORD totalLen = 0;bool bFile = false;for(int i = 0; i < PostParamVec.size(); i++){bFile = PostParamVec[i].filename.IsEmpty() ? false:true;CString temp;if(bFile){temp.Format(_T("\r\n--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n"),cstrBoundary,PostParamVec[i].name,PostParamVec[i].filename,PostParamVec[i].ContentType);}else{temp.Format(_T("\r\n--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s"),cstrBoundary,PostParamVec[i].name,PostParamVec[i].content);}// post請求時,有的參數值可能帶中文,需要unicode->utf8,寫數據的時候同理char* pData = NULL;CUtility::ConvertUnicodeToUTF8(temp,pData);if(pData){totalLen += ::strlen(pData);delete[] pData;pData = NULL;}if(bFile){CFile myFile;CFileException fileException;if ( !myFile.Open( PostParamVec[i].filename, CFile::modeRead, &fileException ) ){CString errMsg;errMsg.Format(_T("Can't open file %s, error = %u\n"),PostParamVec[i].filename, fileException.m_cause);TRACE(errMsg); #ifdef TEXT_LOGCUtility::TextLog(_T("CUtility::HttpPostUploadFile"),errMsg); #endif}totalLen += myFile.GetLength();myFile.Close();}}totalLen+= dataEnd.length();pHttpFile->SendRequestEx( (DWORD)totalLen );// 2.往服務器寫數據bFile = false;for(int i = 0; i < PostParamVec.size(); i++){bFile = PostParamVec[i].filename.IsEmpty() ? false:true;CString temp;if(bFile){temp.Format(_T("\r\n--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n"),cstrBoundary,PostParamVec[i].name,PostParamVec[i].filename,PostParamVec[i].ContentType);}else{temp.Format(_T("\r\n--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s"),cstrBoundary,PostParamVec[i].name,PostParamVec[i].content);}// post請求時,有的參數值可能帶中文,需要unicode->utf8,寫數據的時候同理char* pData = NULL;CUtility::ConvertUnicodeToUTF8(temp,pData);if(pData){int pDataLen = ::strlen(pData);pHttpFile->Write(pData, pDataLen);delete[] pData;pData = NULL;}if(bFile){CFile myFile;CFileException fileException;if ( !myFile.Open( PostParamVec[i].filename, CFile::modeRead, &fileException ) ){TRACE( _T("Can't open file %s, error = %u\n"),PostParamVec[i].filename, fileException.m_cause );}int bufflength = 4 * 1024;byte* buffer = new byte[bufflength];memset(buffer,0,bufflength);int byteRead = 0;while ((byteRead = myFile.Read(buffer, bufflength)) != 0){pHttpFile->Write(buffer, byteRead);memset(buffer,0,bufflength);}myFile.Close();}}pHttpFile->Write(dataEnd.c_str(),dataEnd.length());pHttpFile->EndRequest( );#ifdef TEXT_LOGCUtility::TextLog(_T("CUtility::HttpPostUploadFile"),_T(" EndRequest ok")); #endifDWORD dwStateCode = 0;pHttpFile->QueryInfoStatusCode(dwStateCode);if (dwStateCode != HTTP_STATUS_OK ){CString str;str.Format(_T("服務器返回狀態碼:%d !!!\n"), dwStateCode); #ifdef TEXT_LOGCUtility::TextLog(_T("CUtility::HttpPostUploadFile"),str); #endifTRACE(str);// 認為出錯strErrMsg = str;}else if(dwStateCode == HTTP_STATUS_OK ){// 3.處理響應數據CString contentType ;pHttpFile->QueryInfo(HTTP_QUERY_CONTENT_TYPE, contentType );if(contentType.Find(_T("application/json")) >= 0){bResult = true;DWORD contentLength = 0;BOOL b = pHttpFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH,contentLength);if(contentLength == 0){contentLength = 4096;}char *pReadBuf = new char[contentLength +1];memset(pReadBuf, 0, contentLength +1);DWORD nRead = pHttpFile->Read(pReadBuf, contentLength);// 響應數據可能帶中文,需要utf8->unicodeCUtility::ConvertUTF8ToUnicode(pReadBuf, strResponse,nRead);delete []pReadBuf;pReadBuf = NULL;}else{// 認為出錯strErrMsg = _T("Content-type錯誤!!!"); #ifdef TEXT_LOGCUtility::TextLog(_T("CUtility::HttpPostUploadFile"),strErrMsg); #endifTRACE(strErrMsg);}#ifdef TEXT_LOGCUtility::TextLog(L"CUtility::HttpPostUploadFile",L"QueryInfoStatusCode ok" ); #endif}}}catch (CInternetException* e){TCHAR szError[1024] = {0};e->GetErrorMessage(szError,1024);DWORD errorCode = e->m_dwError;CString str;str.Format(_T("errorMsg:%s,m_dwError=%d"), szError,errorCode); #ifdef TEXT_LOGCUtility::TextLog(_T("CUtility::HttpPostUploadFile,HttpPostUploadFile exception"),str); #endife->Delete();TRACE( _T("\r----------------internet error:%s\r"),str );strErrMsg = str;}if(pHttpConnection != NULL){pHttpConnection->Close();delete pHttpConnection;pHttpConnection = NULL;}session.Close();if(pHttpFile != NULL){pHttpFile->Close();delete pHttpFile;pHttpFile = NULL;}return bResult; }接口調用地方代碼
void CClienTestDlg::OnBnClickedButton3() {// 使用MFC 方式上傳文件PostUpFileValVector vec;PostUpFileValStruct param;param.name = _T("tranCode");param.content = _T("2007");vec.push_back(param);param.name = _T("busiNo");param.content = _T("20200527000000209096");vec.push_back(param);param.name = _T("sysId");param.content = _T("ACP");vec.push_back(param);param.name = _T("operaId");param.content = _T("00300");vec.push_back(param);param.name = _T("branchNo");param.content = _T("00300");vec.push_back(param);param.name = _T("billId");param.content = _T("ACP04");vec.push_back(param);param.name = _T("file");param.content = _T("");param.filename = _T("D:\\20200520120631.jpg");param.ContentType = _T("image/jpeg");vec.push_back(param);CString upUrl = _T("http://172.xx.xxx.xxx:xxxxx/file/xxxxx");CString cstrResponse;CString cstrErrMsg;std::map<CString,CString> requestHeads;bool bResult = CUtility::HttpPostUploadFile(vec,upUrl,requestHeads,cstrResponse,cstrErrMsg);CString strMsg;if(bResult){strMsg.Format(_T("文件上傳成功,響應信息:%s"),cstrResponse);MessageBox(strMsg,_T("文件上傳"));}else{strMsg.Format(_T("文件上傳失敗!!!錯誤提示信息:%s"),cstrErrMsg);MessageBox(strMsg,_T("文件上傳"));}return; }總結
以上是生活随笔為你收集整理的MFC使用http post请求上传文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT5_chart_常见几种图形
- 下一篇: matlab实现一/多元非线性回归