使用Http協議Post上傳文件


轉載:http://www.cnblogs.com/softidea/p/5745369.html

轉載:https://blog.csdn.net/huanongying131/article/details/78426219

轉載:https://blog.csdn.net/shaderdx/article/details/28406033

轉載:https://blog.csdn.net/yamahayamede/article/details/79561119(http post 上傳文件格式)

轉載:https://blog.csdn.net/Ideality_hunter/article/details/82971483(示例)

轉載:https://blog.csdn.net/a29562268/article/details/53932582(MFC 支持 Https 示例)

轉載:http://www.cnso.org/197/3835.html

轉載:https://blog.csdn.net/u010256388/article/details/68491509

1.使用場景

       公司產品需要做一個關於收集程序崩潰信息的模塊(BugReport),需要客戶端程序在崩潰發生后將崩潰日志以及轉儲文件發送到后台。

2.http 格式

  multipart/form-data

        這又是一個常見的 POST 數據提交的方式。我們使用表單上傳文件時,必須讓 <form> 表單的enctype 等於 multipart/form-data。直接來看一個請求示例:

BASHPOST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"  name:key 兩個空行 \r\n\r\n
title                                        value:title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

這個例子稍微復雜點。首先生成了一個 boundary 用於分割不同的字段,為了避免與正文內容重復,boundary 很長很復雜。然后 Content-Type 里指明了數據是以 multipart/form-data 來編碼,本次請求的 boundary 是什么內容。消息主體里按照字段個數又分為多個結構類似的部分,每部分都是以 --boundary 開始,緊接着是內容描述信息,然后是回車,最后是字段具體內容(文本或二進制)。如果傳輸的是文件,還要包含文件名和文件類型信息。消息主體最后以 --boundary-- 標示結束。
關於 multipart/form-data 的詳細定義,請前往 rfc1867 查看。

這種方式一般用來上傳文件,各大服務端語言對它也有着良好的支持。

上面提到的這兩種 POST 數據的方式,都是瀏覽器原生支持的,而且現階段標准中原生 <form> 表單也只支持這兩種方式(通過 <form> 元素的 enctype 屬性指定,默認為 application/x-www-form-urlencoded。其實 enctype 還支持 text/plain,不過用得非常少)。

隨着越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。

 

 

MFC代碼:

#include <iostream>
#include <afxinet.h>
#include <Windows.h>
#include <atlbase.h>
#include <shlwapi.h>
#include <string>
#include <vector>
using namespace std;

#pragma warning(disable : 4996)

wstring Utf8ToUnicode(const char* szU8)
{
    if (szU8 == NULL)
        return L"";

    if (strlen(szU8) == 0)
        return L"";

    //預轉換,得到所需空間的大小;
    int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);

    //分配空間要給'\0'留個空間,MultiByteToWideChar不會給'\0'空間
    wchar_t* wszString = new wchar_t[wcsLen + 1];

    //轉換
    ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);

    //最后加上'\0'
    wszString[wcsLen] = '\0';

    wstring unicodeString = wszString;

    delete[] wszString;
    wszString = NULL;

    return unicodeString;
}

string UnicodeToASNI(const wchar_t* unicode)
{
    if (unicode == NULL)
        return "";

    if (wcslen(unicode) == 0)
        return "";

    int len;
    len = WideCharToMultiByte(CP_ACP, 0, unicode, -1, NULL, 0, NULL, NULL);
    char* szUtf8 = (char*)malloc(len + 1);
    memset(szUtf8, 0, len + 1);
    WideCharToMultiByte(CP_ACP, 0, unicode, -1, szUtf8, len, NULL, NULL);

    string sRet = szUtf8;
    free(szUtf8);
    szUtf8 = NULL;
    return sRet;
}

//ANSI ---> Unicode
std::wstring ANSIToUnicode(const string& str)
{
    int  Length = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);

    if (Length == 0) return NULL;

    wchar_t *  pUnicode = new  wchar_t[Length + 1];

    memset(pUnicode, 0, (Length + 1) * sizeof(wchar_t));

    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, (LPWSTR)pUnicode, Length);

    pUnicode[Length] = 0;

    wstring  wstr = (wchar_t*)pUnicode;

    delete[]  pUnicode;
    pUnicode = NULL;

    return  wstr;
}

string UnicodeToUtf8(const wstring& wstr)
{
    // 預算-緩沖區中多字節的長度  
    int ansiiLen = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    // 給指向緩沖區的指針變量分配內存  
    char *pAssii = (char*)malloc(sizeof(char)*ansiiLen);
    // 開始向緩沖區轉換字節  
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, pAssii, ansiiLen, nullptr, nullptr);
    string ret_str = pAssii;
    free(pAssii);
    return ret_str;
}


BOOL CSRCMBugReportDlg::UploadHttpFile(const CString& strUploadFileURL, const CString & strCrashFilePath, bool isHttps)
{
    ASSERT(strUploadFileURL != "");

    ASSERT(strCrashFilePath != "");

    CInternetSession session(L"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", 0);
    CHttpConnection* pHttpConnection = NULL;
    CHttpFile* pHttpFile = NULL;
    try
    {
        DWORD ServerType = 0;
        CString strServer, strObject;
        INTERNET_PORT wPort;
        CFile file;

        if (AfxParseURL(strUploadFileURL, ServerType, strServer, strObject, wPort))
        {
            //打開本地待上傳文件
            if (!file.Open(strCrashFilePath, CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))//如果文件路徑有中文必須用ANSIToUnicode轉成寬字節
            {
                CString errText;
                errText.Format(L"打開本地文件%s失敗", strCrashFilePath);
                theLogger.TraceError(errText);

                ::PostMessage(this->GetSafeHwnd(), WM_EXIT_BUGREPORT, 0, 0);//完成退出BugReport
                return FALSE;
            }

            const int nTimeOut = 5000;//5秒超時
            session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, nTimeOut); //重試之間的等待延時
            session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);   //重試1次數

            if (isHttps)
            {
                pHttpConnection = session.GetHttpConnection(strServer, INTERNET_FLAG_SECURE, wPort, NULL, NULL); //連接Https服務器
            }
            else
            {
                pHttpConnection = session.GetHttpConnection(strServer, wPort); //取得一個Http聯接
            }

            if (pHttpConnection)
            {
if(isHttps)
{
pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST,strObject,NULL,1
NULL,NULL,INTERNET_FLAG_SECURE|
INTERNET_FLAG_EXISTING_CONNECT|
INTERNET_FLAG_RELOAD |
INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
INTERNET_FLAG_NO_COOKIES
);//打開請求 ,這幾個標識都要加上
}
else
{ pHttpFile
= pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, strObject); } if (pHttpFile) { /*szBoundary,任意的一個串,用以標識body中的數據*/ //該串需要夠復雜,用以區別當前請求包中的數據 const char * szBoundary = "----------------------------096304854221961038114493"; /*構造頭部*/ pHttpFile->AddRequestHeaders(L"Accept:*/*"); pHttpFile->AddRequestHeaders(L"Accept-Encoding:gzip, deflate"); //構造頭中的Content-Type項,其中需要填上boundary CString szContentType = L"Content-Type:multipart/form-data; boundary="; szContentType += szBoundary; pHttpFile->AddRequestHeaders(szContentType.GetBuffer(0)); /*打開文件,合成文件頭部數據,取出文件數據,以及合成尾部數據,計算總長度*/ struct DataInfo{ char* pBuf; DWORD BufLen; DataInfo(){ ZeroMemory(this, sizeof(DataInfo)); } }; std::vector<DataInfo> DataInfoList; wstring strAccount = L"test@163.com"; wstring strMsg = L"崩潰了"; CString FileName = file.GetFileName(); char key1[1024] = { 0 }; sprintf(key1, "\r\n------------------------------096304854221961038114493\r\n" "Content-Disposition: form-data;name=\"Account\" \r\n\r\n%s", UnicodeToUtf8(strAccount).c_str());//如果value有中文必須轉成UTF-8 char key2[1024] = { 0 }; sprintf(key2, "\r\n------------------------------096304854221961038114493\r\n" "Content-Disposition: form-data;name=\"Message\" \r\n\r\n%s", UnicodeToUtf8(strMsg).c_str());//如果value有中文必須轉成UTF-8 char strFileconnet[1024]= { 0 }; sprintf(strFileconnet, "\r\n------------------------------096304854221961038114493\r\n" "Content-Disposition:form-data;name=\"file\";filename=\"%s.zip\"\r\n" "Content-Type:application/zip\r\n\r\n", UnicodeToUtf8(FileName).c_str()); //拼body string strconnet = key1; strconnet += key2; strconnet += strFileconnet; DWORD dwTotalBodyLen = 0; //每個文件的頭部信息 DataInfo preDi; preDi.BufLen = strlen(strconnet.c_str()); preDi.pBuf = new char[preDi.BufLen]; memcpy(preDi.pBuf, strconnet.c_str(), preDi.BufLen); DataInfoList.push_back(preDi); dwTotalBodyLen += preDi.BufLen; //文件內容 DataInfo ContDi; ContDi.BufLen = (DWORD)file.GetLength(); ContDi.pBuf = new char[ContDi.BufLen]; file.Read(ContDi.pBuf, ContDi.BufLen); DataInfoList.push_back(ContDi); file.Close(); dwTotalBodyLen += ContDi.BufLen; //尾長度 const char* szEndContent = "\r\n" "------------------------------096304854221961038114493--\r\n"; DataInfo EndDi; EndDi.BufLen = strlen(szEndContent); EndDi.pBuf = new char[EndDi.BufLen]; memcpy(EndDi.pBuf, szEndContent, EndDi.BufLen); DataInfoList.push_back(EndDi); dwTotalBodyLen += EndDi.BufLen; /*發送數據*/ pHttpFile->SendRequestEx(dwTotalBodyLen, HSR_SYNC | HSR_INITIATE); std::vector<DataInfo>::iterator it3 = DataInfoList.begin(); for (; it3 != DataInfoList.end(); it3++) { pHttpFile->Write(it3->pBuf, it3->BufLen); delete[]it3->pBuf; } pHttpFile->EndRequest(HSR_SYNC); /*獲取狀態*/ char* RetStr = NULL; DWORD dwStatusCode = -1; pHttpFile->QueryInfoStatusCode(dwStatusCode); if (dwStatusCode == HTTP_STATUS_OK) { //讀返回 RetStr = new char[(DWORD)pHttpFile->GetLength()]; pHttpFile->Read(RetStr, (UINT)pHttpFile->GetLength()); if (pHttpFile) delete pHttpFile; if (pHttpConnection) delete pHttpConnection; session.Close(); ::PostMessage(this->GetSafeHwnd(), WM_EXIT_BUGREPORT, 0, 0);//完成退出BugReport return TRUE; } else { if (pHttpFile != NULL) { pHttpFile->Close(); delete pHttpFile; pHttpFile = NULL; } if (pHttpConnection != NULL) { pHttpConnection->Close(); delete pHttpConnection; pHttpConnection = NULL; } session.Close(); CString errText; errText.Format(L"POST出錯,錯誤碼:%d", dwStatusCode); theLogger.TraceError(errText); ::PostMessage(this->GetSafeHwnd(), WM_EXIT_BUGREPORT, 0, 0);//完成退出BugReport return FALSE; } } } } } catch (...) { if (pHttpFile != NULL) { pHttpFile->Close(); delete pHttpFile; pHttpFile = NULL; } if (pHttpConnection != NULL) { pHttpConnection->Close(); delete pHttpConnection; pHttpConnection = NULL; } session.Close(); DWORD dwError = GetLastError(); CString str; str.Format(L"Unknow Exception occur %d", dwError); theLogger.TraceError(str); ::PostMessage(this->GetSafeHwnd(), WM_EXIT_BUGREPORT, 0, 0);//完成退出BugReport return FALSE; } if (pHttpFile != NULL) pHttpFile->Close(); if (pHttpConnection != NULL) pHttpConnection->Close(); session.Close(); ::PostMessage(this->GetSafeHwnd(), WM_EXIT_BUGREPORT, 0, 0);//完成退出BugReport return TRUE; } int main() { wstring url = L"http://localhost:8080"; wstring strFilepath; //獲取工作路徑 TCHAR szModulePath[MAX_PATH * 2] = { 0 }; ::GetModuleFileName(NULL, szModulePath, _countof(szModulePath) - 2); PathRemoveFileSpec(szModulePath); strFilepath = szModulePath; strFilepath += L"\\測試.zip"; PostUploadFile(url.c_str(), strFilepath .c_str()); return 0; }

 點擊下載:MFC上傳文件demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM