文章目录

  • 前言
  • 代码

前言

笔者在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请求上传文件相关推荐

  1. php post 文件,PHP响应post请求上传文件的方法_php技巧

    本文实例讲述了PHP响应post请求上传文件的方法.分享给大家供大家参考,具体如下: function send_file($url, $post = '', $file = '') { $eol = ...

  2. 解决python发送multipart/form-data请求上传文件的问题

    解决python发送multipart/form-data请求上传文件的问题 参考文章: (1)解决python发送multipart/form-data请求上传文件的问题 (2)https://ww ...

  3. java 转发上传文件_Java 发送http请求上传文件功能实例

    废话不多说了,直接给大家贴代码了,具体代码如下所示: package wxapi.WxHelper; import java.io.BufferedReader; import java.io.Dat ...

  4. Android中使用retrofit2进行网络get请求查询数据和post请求上传文件

    场景 Retrofit2 Retrofit 是对 OkHttp 的封装,是主流的网络框架. 适用于Android 和 Java 的类型安全的HTTP客户端,由Square提供的. Retrofit是一 ...

  5. SpringMVC实现PUT请求上传文件

    在JQuery中,我们可以进行REST ful中delete和put的请求,但是在java EE标准中,默认只有在POST请求的时候,servlet 才会通过getparameter()方法取得请求体 ...

  6. java发送post请求上传文件和json数据

    java发送post请求上传文件和json数据 因为第三方的上传服务post参数使用了两个@requestpart参数. 但是feign不可以使用两个@requestpart参数.会报错:java.l ...

  7. Http post请求上传文件

    最近遇到一个需求,需要调用HttpClient实现上传文件的功能,Content-Type为form-data形式 研究一番,代码如下: /*** multipart/form-data 上传文件方式 ...

  8. java http post上传文件_Java通过HTTP POST请求上传文件

    我想创建一个Java应用程序来执行以下在Windows上的CURL中工作.Java通过HTTP POST请求上传文件 curl -x XXX.XXX.XXX.XXX:8080 -X POST --da ...

  9. Python请求上传文件接口

    Python 请求上传文件接口 def post_multipart_request():params = {"key1": "value1", "k ...

最新文章

  1. [Usaco2007 Dec]穿越泥地[bfs][水]
  2. OpenCV学习笔记(三十六)——Kalman滤波做运动目标跟踪 OpenCV学习笔记(三十七)——实用函数、系统函数、宏core OpenCV学习笔记(三十八)——显示当前FPS OpenC
  3. 《江湖X:汉家江湖》两万人在线服务器架构
  4. webpack4配置基础
  5. 基于消息的异步套接字
  6. WordPress插件:WP-China-Yes解决国内访问官网慢的方法
  7. 白话空间统计二十四:地理加权回归(六)ArcGIS的GWR工具参数说明一
  8. 通过jsp实现省市区县四级联动菜单
  9. 制作Win10 U盘版移动便携系统
  10. 获取 pdf 中某个 关键字的位置
  11. Vitis开发笔记:使用分区工具Gparted将SD卡分为BOOT和RoofFS
  12. Python 中还藏着这些稀奇古怪的东西...
  13. JVM学习笔记(4)-运行时数据区详解之程序计数器与虚拟机栈
  14. select 选择框里最多可是多选的情况
  15. 【架构思维】:设计服务降级的思路与方法
  16. win2003控制面板不见了,打开“控制面板”的方法,安全策略
  17. 手把手教你部署Docker(手撸官网)
  18. tar linux 跳过解压,【linux命令】linux解压压缩命令tar详解以及压缩的时候如何跳过某一个压缩目录或文件...
  19. 数据分析与预测(二)——pandas 函数read_csv解析
  20. 百度网盘取消自动续费(详细过程)

热门文章

  1. 12个ggplot2扩展包帮你实现更强大的可视化
  2. Stats mac免费版 2.4.9(支持M1驱动器的mac,最新macOS系统)
  3. ht1621b和单片机电平匹配_有备无患,单片机面试问题集
  4. Python中Text函数常用增删改查阅用法实例汇总
  5. 【原型设计】第四节:Axure RP9 交面交互的使用说明 01 打开链接交互效果
  6. js网页文件资源加载器
  7. 故宫学生网页设计作品 dreamweaver作业静态HTML网页设计模板 旅游景点网页作业制作
  8. 电脑键盘练习_用键盘打字怎样才能练得快,有什么窍门没?
  9. 信息安全工程师笔记-综合知识冲刺(一)
  10. 1NF 2NF 3NF BCNF