已经有将近6年时间没写过MFC了,想想以前我也是写VC++入门程序开发的,那时候写协议栈、搞语音编码、做视频压缩和实时数据传输,相比现在更多偏业务的开发,那时候搞得都是非常技术的东西。眨眼间,MFC已经退出舞台,就连微软也在主推C#.net,曾经风光无限的MFC开发现如今已经几乎消失;ActiveX有java的applet来替代,MFC桌面应用程序也由更简单友好的C#取代,而软件开发的趋势早已经从传统的C/S过渡到B/S,云等等。技术的日新月异逼迫IT人不得不马不停蹄的学习,否则,一不留神就会被这个时代所抛弃,被后来者所碾压。时刻保持一颗不断学习,谦逊,对技术敬畏的心,才能在这条路上才能走的更远走得稳。

因为一些缘故,刚毕业时候带我的老领导找我紧急支援一点东西,具体需求就是用MFC写一个抓屏程序,把当前屏幕抓取下来,实时提交给服务器,然后再由服务中转推送给客户端去显示,可以简单理解为一个桌面监控程序。让我帮忙处理抓屏和POST提交文件的功能。我听了之后本来想拒绝,尽管老领导对我的印象还是VC++coder,但毕竟我已经好多年不写MFC,着实没有信心搞定。可是又不想辜负老领导对我期望,毕竟是他是我IT路上的引路人,所以最后还答应了。然后这个周末就一直在网上搜相关的解决方案,所幸,这些东西并不复杂,关于截屏的代码非常多,大多都能满足需求。但是最后关于POST提交文件这一块遇到了困难,网上能搜索到很多关于MFC使用post上传下载文件的代码,但是几乎没有哪一个能真正用的。

我这里记录并分享一个我最后搜索并优化的方案,先看代码

CString CScreenPushDlg::MakeRequestHeaders(CString& strBoundary)
{
CString strFormat;
CString strData;
strFormat = _T("Content-Type: multipart/form-data; boundary=%s\r\n");
strData.Format(strFormat, strBoundary);
return strData;
}

CString CScreenPushDlg::MakePreFileData(CString& strBoundary, CString& strFileName, int iRecordID)
{
CString strFormat;
CString strData;
strFormat += _T("--%s");
strFormat += _T("\r\n");
strFormat += _T("Content-Disposition: form-data; name=\"job_number\"");
strFormat += _T("\r\n\r\n");
strFormat += _T("%d");
strFormat += _T("\r\n");
strFormat += _T("--%s");
strFormat += _T("\r\n");
strFormat += _T("Content-Disposition: form-data; name=\"image\"; filename=\"%s\"");
strFormat += _T("\r\n");
//strFormat += _T("Content-Type: audio/wav");
strFormat += _T("Content-Type: image/png");
strFormat += _T("\r\n");
strFormat += _T("Content-Transfer-Encoding: binary");
strFormat += _T("\r\n\r\n");
strData.Format(strFormat, strBoundary, iRecordID, strBoundary, strFileName);
return strData;
}

CString CScreenPushDlg::MakePostFileData(CString& strBoundary)
{
CString strFormat;
CString strData;
strFormat = _T("\r\n");
strFormat += _T("--%s");
strFormat += _T("\r\n");
strFormat += _T("Content-Disposition: form-data; name=\"act\"");
strFormat += _T("\r\n\r\n");
strFormat += _T("upload_img");
strFormat += _T("\r\n");
strFormat += _T("--%s--");
strFormat += _T("\r\n");
strData.Format(strFormat, strBoundary, strBoundary);
return strData;
}
//上传文件数据至HTTP服务器
int CScreenPushDlg::UploadFile(const CString &szServerURL, const CString &szUploadFileName)
{
if (szServerURL.IsEmpty() || szUploadFileName.IsEmpty())
{
return -1;
}
BOOL bRet = FALSE;
DWORD dwServiceType = 0;
CString strServer = _T("");
CString strObject = _T("");
INTERNET_PORT nPort = 0;
bRet = AfxParseURL(szServerURL, dwServiceType, strServer, strObject, nPort);
if(!bRet)
{
return -2;
}

int nRet = 0;
CInternetSession Session;
CHttpConnection * pHttpConnection = NULL;
CFile fTrack;
CHttpFile* pHTTPFile = NULL;
CString strHTTPBoundary = _T("");
CString strPreFileData = _T("");
CString strPostFileData = _T("");
DWORD dwTotalRequestLength;
DWORD dwChunkLength = 0;
DWORD dwReadLength = 0;
DWORD dwResponseLength = 0;
TCHAR szError[MAX_PATH] = {0};
void* pBuffer = NULL;
LPSTR szResponse = NULL;
CString strResponse = _T("");
BOOL bSuccess = TRUE;
CString strDebugMessage = _T("");

if (FALSE == fTrack.Open(szUploadFileName, CFile::modeRead | CFile::shareDenyWrite))
{
//AfxMessageBox(_T("Unable to open the file."));
return -3;
}
int nPos = szUploadFileName.ReverseFind('\\');
CString strFileName = szUploadFileName.Mid(nPos+1);
int iRecordID = _ttoi(m_Num);
strHTTPBoundary = _T("----istroop----");
strPreFileData = MakePreFileData(strHTTPBoundary, strFileName, iRecordID);
strPostFileData = MakePostFileData(strHTTPBoundary);
dwTotalRequestLength = strPreFileData.GetLength() + strPostFileData.GetLength() + fTrack.GetLength();
dwChunkLength = 64 * 1024;
pBuffer = malloc(dwChunkLength);
if (NULL == pBuffer)
{
fTrack.Close();
return -4;
}
try
{
pHttpConnection = Session.GetHttpConnection(strServer,nPort);
pHTTPFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject);
pHTTPFile->AddRequestHeaders(MakeRequestHeaders(strHTTPBoundary));
pHTTPFile->SendRequestEx(dwTotalRequestLength, HSR_SYNC | HSR_INITIATE);
#ifdef _UNICODE
USES_CONVERSION;
pHTTPFile->Write(W2A(strPreFileData), strPreFileData.GetLength());
#else
pHTTPFile->Write((LPSTR)(LPCSTR)strPreFileData, strPreFileData.GetLength());
#endif
dwReadLength = -1;
DWORD m_dwUploadSize = 0;
while (0 != dwReadLength)
{
// strDebugMessage.Format(_T("%u / %un"), fTrack.GetPosition(), fTrack.GetLength());
// TRACE(strDebugMessage);
dwReadLength = fTrack.Read(pBuffer, dwChunkLength);
if (0 != dwReadLength)
{
m_dwUploadSize += dwReadLength;
pHTTPFile->Write(pBuffer, dwReadLength);
}
}
#ifdef _UNICODE
pHTTPFile->Write(W2A(strPostFileData), strPostFileData.GetLength());
#else
pHTTPFile->Write((LPSTR)(LPCSTR)strPostFileData, strPostFileData.GetLength());
#endif
pHTTPFile->EndRequest(HSR_SYNC);
dwResponseLength = pHTTPFile->GetLength();
while (0 != dwResponseLength)
{
szResponse = (LPSTR)malloc(dwResponseLength + 1);
szResponse[dwResponseLength] = '\0';
pHTTPFile->Read(szResponse, dwResponseLength);
strResponse += szResponse;
free(szResponse);
szResponse = NULL;
dwResponseLength = pHTTPFile->GetLength();
}
//strResponse;
}
catch (CException* e)
{
// e->GetErrorMessage(szError, MAX_PATH);
// e->Delete();
nRet = -5;
}
if (NULL != pHTTPFile)
{
pHTTPFile->Close();
delete pHTTPFile;
pHTTPFile = NULL;
}

fTrack.Close();
if (NULL != pBuffer)
{
free(pBuffer);
pBuffer = NULL;
}

return nRet;
}
这段代码相比网上能搜到的大多数方案,最大的区别在于写了一个MakeRequestHeaders和MakePreFileData方法。

因为这些年主要写java和前端js开发,java中有httpclient可以直接发起post/get请求,非常方便,js中有ajax、axios等都非常方便的发起的请求,类似设置header、Content-type、入参data等都有很简单明了的方法,但是VC++中使用CHttpFile类进行post请求却相对复杂些,主要在于api封装的颗粒度。我们先来看以下下面这两张截图:

(截图一)

(截图二)

这是一个ajax发起的上传文件并携带参数的POST请求,我以前都是只关注截图一,从来没考虑也没看过截图二中的内容。其实对于MFC中的POST请求,就需要我们自己封装原始的数据,即自己封装好截图二中的数据格式,类似Content-Disposition、Content-Type、通过name指定参数名,通过filename指定具体文件,还要设置FormBoundar等。这些东西在java、js中时绝对不会接触到的。而MakePreFileData正是起到这个作用。

大概记录到这里吧,开发语言这东西,真的是不写就会生疏,以前写VC++都能盲打api,现在看数据类型的声明类都觉得生疏,真的是感慨万千。毕业的这几年收获最大的大概是不像以前那么玻璃心,见不得别人和自己相左的意见;但是相对的,我觉得那时候的自己更自负,而现在我却有点过分自知,甚至不那么自信。

认清自己不难,难的是知道自己的不足和缺点后怎么鞭策自己去改变。
---------------------

MFC中使用post提交form-data上传文件的更多相关文章

  1. 前端 - jquery方式 / iframe +form 方式 上传文件

    环境与上一章一样 jquery 方式上传文件: HTML代码 {#html代码开始#} <input type="file" id="img" > ...

  2. 在 github 中新建仓库后,如何上传文件到这个仓库里面。

    在 github 中新建仓库后,如何上传文件到这个仓库里面. libin@hglibin MINGW64 /e/github.io (master) $ git remote libin@hglibi ...

  3. form 为什么上传文件enctype现场

    FORM要素enctype属性指定表单数据server当提交所使用的编码类型,默认默认值它是"application/x-www-form-urlencoded". 这样的编码方式 ...

  4. django Form组件 上传文件

    上传文件 注意:FORM表单提交文件要有一个参数enctype="multipart/form-data" 普通上传: urls: url(r'^f1/',views.f1), u ...

  5. 通过ajax提交表单上传文件

    //这是看的大神的.//原地址:https://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html $("#sub" ...

  6. Ajax提交用FormData()上传文件

    1.form声明如下 2.ajax设置如下 var formData = new FormData(document.getElementById("form")); $.ajax ...

  7. CentOS7中利用Xshell6向虚拟机本地上传文件

    环境交代 Linux系统:CentOS7, Xshell版本:6 操作步骤 下面我们以一个文件上传来演示用法 第一步 建立连接,这里不多说 在Xshell中点击如下图标,或者直接按 Alt+Ctrl+ ...

  8. [插件] 如何在一个页面中使用多个SWFUpload对象上传文件

    首先需要引入相应的样式和JS文件,还需要借助jQuery的js 提供下载路径:http://pan.baidu.com/s/1EUzca ① 引入js <script type="te ...

  9. libcurl提交表单上传文件

    不多说了,curl的http上传文件代码示例,有需要的可以参考. int http_post_file(const char *url, const char *user, const char *p ...

  10. Jquery.form异步上传文件常见问题解决

    Jquery.form常用方法我就不多说,主要说一下在使用过程中碰到的问题 1.提示 “xxxx” is not define 或者"xxx" is not a function ...

随机推荐

  1. 设计模式-(12)迭代器模式 (swift版)

    一,概念 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示.迭代器模式属于行为型 ...

  2. Local Databases with SQLiteOpenHelper

    Overview For maximum control over local data, developers can use SQLite directly by leveraging SQLit ...

  3. Database Firewall——mysql也是支持的

    Database Firewall The most impressive feature of MySQL security is the Database Firewall. The firewa ...

  4. BZOJ_3172_[Tjoi2013]单词_后缀自动机

    BZOJ_3172_[Tjoi2013]单词_后缀自动机 Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. ...

  5. Outlook 2007 发送邮件

    4 登入以投票 Hi, http://social.msdn.microsoft.com/Forums/zh-TW/6c063b27-7e8a-4963-ad5f-ce7e5ffb2c64/how-t ...

  6. 前端之html第一天

    一.内容

  7. linux更改用户名,域名(转载)

    转自:http://huangro.iteye.com/blog/365975 1. 我们在root权限下,使用命令: usermod -l new_user_name old_user_name 即 ...

  8. 部分安卓微信浏览器无法触发onchange事件

    这是安卓微信的一个遗留问题. 解决办法很简单: 将input标签 <input type=“file" name="image" accept="imag ...

  9. Six degrees of Kevin Bacon

    转自:https://blog.csdn.net/a17865569022/article/details/78766867 Input* Line 1: Two space-separated in ...

  10. [ZOJ1961]Let it Bead

    Description "Let it Bead" company is located upstairs at 700 Cannery Row in Monterey, CA. ...