欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

MFC使用http post请求上传文件

发布时间:2025/3/15 编程问答 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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请求上传文件的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。