文档章节

学习笔记:MFC实现http协议传输图片和文本

小菜先生
 小菜先生
发布于 2015/06/06 19:08
字数 1787
阅读 47
收藏 1
要将自己的文件通过http协议进行上传到Web服务器,就必须进行数据封装,也就是加上http协议的包头,数据前加上数据信息描述,协议尾三部分,一边服务端进行解析。在我们本次项目中,我需要控制Ip摄像头进行拍照,将抓取的图片通过Http协议传送到另一个人负责的服务器端。我用的的时MFC,完成控制客户端开发。
首先他给了我他要求的包头,数据描述,结尾符格式,让我按找C++来实现。首先贴出他的Java封装代码:
给的Java格式封装,要求进行C++仿写:
public class SocketHttpRequester {
    /**
     * 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能:
     *   <FORM METHOD=POST ACTION="http://192.168.1.101:8083/upload/servlet/UploadServlet" enctype="multipart/form-data">
            <INPUT TYPE="text" NAME="name">
            <INPUT TYPE="text" NAME="id">
            <input type="file" name="imagefile"/>
            <input type="file" name="zip"/>
         </FORM>
     * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.iteye.cn或http://192.168.1.101:8083这样的路径测试)
     * @param params 请求参数 key为参数名,value为参数值
     * @param file 上传文件
     */
    public static String post(String path, Map<String, String> params, FormFile[] files) throws Exception{     
        final String BOUNDARY = "---------------------------7da2137580612"; //数据分隔线
        final String endline = "--" + BOUNDARY + "--\r\n";//数据结束标志
        
        int fileDataLength = 0;
        if (files!=null||files.length>0) {
        for(FormFile uploadFile : files){//得到文件类型数据的总长度
            StringBuilder fileExplain = new StringBuilder();
             fileExplain.append("--");
             fileExplain.append(BOUNDARY);
             fileExplain.append("\r\n");
             fileExplain.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
             fileExplain.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
             fileExplain.append("\r\n");
             fileDataLength += fileExplain.length();
             
            if(uploadFile.getInStream()!=null){
                fileDataLength += uploadFile.getFile().length();
             }else{
                 fileDataLength += uploadFile.getData().length;
             }
        }
        }
        StringBuilder textEntity = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {//构造文本类型参数的实体数据
            textEntity.append("--");
            textEntity.append(BOUNDARY);
            textEntity.append("\r\n");
            textEntity.append("Content-Disposition: form-data; name=\""+ entry.getKey() + "\"\r\n\r\n");
            textEntity.append(entry.getValue());
            textEntity.append("\r\n");
        }
        //计算传输给服务器的实体数据总长度
        int dataLength = textEntity.toString().getBytes().length + fileDataLength +  endline.getBytes().length;
    
        URL url = new URL(path);
        int port = url.getPort()==-1 ? 80 : url.getPort();
        Socket socket = new Socket(InetAddress.getByName(url.getHost()), port);           
        OutputStream outStream = socket.getOutputStream();
        //下面完成HTTP请求头的发送
        String requestmethod = "POST "+ url.getPath()+" HTTP/1.1\r\n";
        outStream.write(requestmethod.getBytes());
        String accept = "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n";
        outStream.write(accept.getBytes());
        String language = "Accept-Language: zh-CN\r\n";
        outStream.write(language.getBytes());
        String contenttype = "Content-Type: multipart/form-data; boundary="+ BOUNDARY+ "\r\n";
        outStream.write(contenttype.getBytes());
        String contentlength = "Content-Length: "+ dataLength + "\r\n";
        outStream.write(contentlength.getBytes());
        String alive = "Connection: Keep-Alive\r\n";
        outStream.write(alive.getBytes());
        String host = "Host: "+ url.getHost() +":"+ port +"\r\n";
        outStream.write(host.getBytes());
        //写完HTTP请求头后根据HTTP协议再写一个回车换行
        outStream.write("\r\n".getBytes());
        //把所有文本类型的实体数据发送出来
        outStream.write(textEntity.toString().getBytes());           
        //把所有文件类型的实体数据发送出来
        for(FormFile uploadFile : files){
            StringBuilder fileEntity = new StringBuilder();
             fileEntity.append("--");
             fileEntity.append(BOUNDARY);
             fileEntity.append("\r\n");
             fileEntity.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
             fileEntity.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
             outStream.write(fileEntity.toString().getBytes());
             if(uploadFile.getInStream()!=null){
                 byte[] buffer = new byte[1024];
                 int len = 0;
                 while((len = uploadFile.getInStream().read(buffer, 0, 1024))!=-1){
                     outStream.write(buffer, 0, len);
                 }
                 uploadFile.getInStream().close();
             }else{
                 outStream.write(uploadFile.getData(), 0, uploadFile.getData().length);
             }
             outStream.write("\r\n".getBytes());
        }
        //下面发送数据结束标志,表示数据已经结束
        outStream.write(endline.getBytes());

进行的进行C++格式封装如下:
1.请求包头处理:

CString CHotpimUploadDlg::MakeRequestHeaders(CString &strBoundary)//包头
{
  CString strFormat;
CString strData;
    CString accept = _T("Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n");
     CString language = _T("Accept-Language: zh-CN\r\n");
    CString contenttype = _T("Content-Type: multipart/form-data; boundary=%s\r\n");//二进制文件传送Content-Type类型为: multipart/form-data
    CString contentlength = _T("Content-Length: %d\r\n");
    CString alive = _T("Connection: Keep-Alive\r\n");
    CString host = _T("Host: 192.168.1.112:8080\r\n");
CString anotherEnter = _T("\r\n");
/* 经过多次尝试发现一开头必须为contenttype,只要这一串字符在开头都可以实现图片上传Web服务器
下面的三种字符排列格式,后两种都可行都是可行的,可根据实际情况添加*/
//strFormat += accept+language+contenttype+contentlength+alive+host+anotherEnter;
//strFormat = contenttype;//这行必须要它标志数据开始,其他包头可根据实际添加
strFormat += contenttype+accept+language+contenttype+contentlength+alive+host+anotherEnter;
//strData.Format(strFormat, strBoundary,m_HeaderStrLen);
strData.Format(strFormat, strBoundary,strBoundary, m_HeaderStrLen);// m_HeaderStrLen = strlen(strPreFileData+endLine);
//strData.Format(strFormat, strBoundary);
    return strData;
}
2.传输数据描述:
CString CHotpimUploadDlg::MakePreFileData(CString &strBoundary, CString &strFileName, int iRecordID)
{
    CString strFormat;
    CString strData;

/* 前面的是需要给服务器的一些参数信息,
包括自定义的时间,参数值,比如我们需要identitynumber,time等,name要与服务器一致*/
strFormat += _T("--%s");
    strFormat += _T("\r\n");
    strFormat += _T("Content-Disposition: form-data; name=\"identitynumber\"");
    strFormat += _T("\r\n\r\n");
strFormat += _T("141040046");//id值
    strFormat += _T("\r\n");
//
strFormat += _T("--%s");
    strFormat += _T("\r\n");
    strFormat += _T("Content-Disposition: form-data; name=\"time\"");
    strFormat += _T("\r\n\r\n");
strFormat += _T("1433323280446");//时间值
    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: application/octet-stream\r\n\r\n");

strData.Format(strFormat, strBoundary, strBoundary, strBoundary, strFileName);
//m_strLen = strlen(strData);
    return strData;
}
3.数据尾部部分:
CString CHotpimUploadDlg::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=\"submitted\"");
    strFormat += _T("\r\n\r\n");
    strFormat += _T("submit");
*/
/* 结尾标示符endLine = "--strBoundary--"  */
    strFormat += _T("\r\n");
    strFormat += _T("--%s--");
    strFormat += _T("\r\n"); 
//strData.Format(strFormat, strBoundary, strBoundary);
strData.Format(strFormat, strBoundary);
    return strData;
}
4.数据发送过程如下:

BOOL CHotpimUploadDlg::SendTrack()//发送数据
{
    CString _mFilePath;
//_mFilePath = "E:\\1.dat";//要传的本地文件地址
//_mFilePath = "E:\\1.pdf";
mFilePath = "E:\\20150602194621.jpg";
    int startp = _mFilePath.ReverseFind('\\');
    int namelen = _mFilePath.GetLength()-startp-1;
   
    CString pcmname = _mFilePath.Mid(startp+1,namelen);
    CString defServerName ="192.168.1.103";//服务器名
    CString defObjectName ="/ShcoolSafer/ArriveMessageAction";//保存的地址
 
 
    // USES_CONVERSION;
    CInternetSession Session;
    CHttpConnection *pHttpConnection = NULL;
    INTERNET_PORT   nPort = 80;
    CFile fTrack;
    CHttpFile* pHTTP;
CString strRequestHeader;
    CString strBoundary ;
    CString strPreFileData ;
    CString strPostFileData ;
    DWORD dwTotalRequestLength;
    DWORD dwChunkLength;
    DWORD dwReadLength;
    DWORD dwResponseLength;
    TCHAR szError[MAX_PATH];
    void* pBuffer = NULL;
    LPSTR szResponse;
    CString strResponse;
    BOOL bSuccess = TRUE;
 
    CString strDebugMessage = _T("");
    
    if (FALSE == fTrack.Open(_mFilePath, CFile::modeRead | CFile::shareDenyWrite))//读出文件 
    {
      AfxMessageBox(_T("Unable to open the file."));
      return FALSE;
    }
 
    int iRecordID = 1;
    strBoundary = _T("---------------------------7da2137580612");//定义边界值
CString endLine = _T("-----------------------------7da2137580612--\r\n");//结尾符
strPreFileData = MakePreFileData(strBoundary, pcmname, iRecordID);
m_HeaderStrLen = strlen(strPreFileData+endLine);
strRequestHeader = MakeRequestHeaders(strBoundary);
    strPostFileData = MakePostFileData(strBoundary);
    dwTotalRequestLength = strPreFileData.GetLength() + strPostFileData.GetLength() + fTrack.GetLength();//计算整个包的总长度
//
dwChunkLength = fTrack.GetLength();
    pBuffer = malloc(dwChunkLength);
 
    if (NULL == pBuffer)
    {
    return FALSE;
    }
 
     try
      {
      pHttpConnection = Session.GetHttpConnection(defServerName,nPort);
      pHTTP = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, defObjectName);
      pHTTP->AddRequestHeaders(strRequestHeader);//发送包头请求
      pHTTP->SendRequestEx(dwTotalRequestLength, HSR_SYNC | HSR_INITIATE);
      
     #ifdef _UNICODE
      pHTTP->Write(W2A(strPreFileData), strPreFileData.GetLength());
    #else
      pHTTP->Write((LPSTR)(LPCSTR)strPreFileData, strPreFileData.GetLength());//写入服务器所需信息
    #endif
      
      dwReadLength = -1;
      while (0 != dwReadLength)
      {
           strDebugMessage.Format(_T("%u / %u\n"), fTrack.GetPosition(), fTrack.GetLength());
           TRACE(strDebugMessage);
           dwReadLength = fTrack.Read(pBuffer, dwChunkLength);//文件内容
           if (0 != dwReadLength)
           {
                pHTTP->Write(pBuffer, dwReadLength);//写入服务器本地文件,用二进制进行传送
           }
      }
      
    #ifdef _UNICODE
      pHTTP->Write(W2A(strPostFileData), strPostFileData.GetLength());
    #else
      pHTTP->Write((LPSTR)(LPCSTR)strPostFileData, strPostFileData.GetLength());
    #endif
      
      pHTTP->EndRequest(HSR_SYNC);
      
      dwResponseLength = pHTTP->GetLength();
      while (0 != dwResponseLength)
      {
        szResponse = (LPSTR)malloc(dwResponseLength + 1);
        szResponse[dwResponseLength] = '\0';
        pHTTP->Read(szResponse, dwResponseLength);
        strResponse += szResponse;
        free(szResponse);
        dwResponseLength = pHTTP->GetLength();
      }
      


      
     } 
     catch (CException* e)
      {
      e->GetErrorMessage(szError, MAX_PATH);
      e->Delete();
      AfxMessageBox(szError);
      bSuccess = FALSE;
      }
 
      pHTTP->Close();
     delete pHTTP;
     
      fTrack.Close();
     
      if (NULL != pBuffer)
      {
      free(pBuffer);
      }
     return bSuccess;
}

5.发送图片时,截取的数据(文本显示)如下:


© 著作权归作者所有

共有 人打赏支持
小菜先生
粉丝 0
博文 2
码字总数 3261
作品 0
杭州
私信 提问
Boost简介

本博客(http://blog.csdn.net/livelylittlefish )贴出作 者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正! Boost简介 是什么? Boost是一个功能强大、构造精巧、跨平台...

晨曦之光
2012/03/09
436
0
1+1=2的 blog 文章索引

百度空间中 原blog部分文章 索引:http://hi.baidu.com/cyclone/home Qt Bugs 通过 Qt Bugs 学习 Qt 似乎是一个不错的方法。 QString之arg使用一则 QTBUG-19027 QMainWindow上下文菜单内存泄...

晨曦之光
2012/05/08
309
0
程序员技术练级攻略

月光博客6月12日发表了《写给新手程序员的一封信》,翻译自《An open letter to those who want to start programming》,我的朋友(他在本站的id是Mailper)告诉我,他希望在酷壳上看到一篇...

张金富
2011/07/19
22.6K
50
我们的CTO给我提出一个这样的难题不知道怎么解决?

我是个java程序员。在做一个项目的服务端开发,客户端为手机app,使用的http协议传输json数据来实现数据交互业务处理,现在又加入了一个C++的视屏监控客户端,需要我提供手机端的用户信息及位...

颖辉小居
2015/12/05
806
9
ActiveMQ-CPP v3.8.3 发布,C++ 的消息服务

ActiveMQ-CPP v3.8.3 发布了,该版本主要是 bug 修复,包括 ConnectionAudit 类的一些线程问题;增强了 SSL 代码允许在包含多个 CN 值的证书中查找域名。详细介绍请看 ChangeLog。 CMS (全称...

红薯
2014/07/19
2K
0

没有更多内容

加载失败,请刷新页面

加载更多

解决各浏览器向url中传中问参数的问题

https://www.cnblogs.com/godtrue/p/4333262.html 后台的处理代码 public static String getUrlnewName( String oldName) {String newName = "";try {String agent = inv.get......

踏破铁鞋无觅处
37分钟前
1
0
微信支付携带证书请求

package utils.wechat; import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.security.KeyStore; import javax.net.ssl.SSLContext;......

猿神出窍
45分钟前
2
0
1093 - You can't specify target table 'xxx' for update in FROM clause, Time: 0.002000s

1093 - You can't specify target table 'xxx' for update in FROM clause, Time: 0.002000s 根据结果集在b_order_copy1 表中删除 DELETE FROM b_order_copy1 WHERE Id in ( SELECT Id FRO......

lwenhao
45分钟前
1
0
JavaScriptCore全面解析

本文由云+社区发表 作者:殷源,专注移动客户端开发,微软Imagine Cup中国区特等奖获得者 JavaScript越来越多地出现在我们客户端开发的视野中,从ReactNative到JSpatch,JavaScript与客户端相...

腾讯云加社区
49分钟前
1
0
Jmeter参数的AES加密使用

在Jmeter日常实践中,大家应该都遇到过接口传参需要加密的情况。以登陆为例,用户名和密码一般都需要进行加密传输,在服务端再进行解密,这样安全系数会更高,但在使用jmeter进行接口测试的时...

程序猿拿Q
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部