使⽤Http协议Post上传⽂件
转载:h
1.使⽤场景
公司产品需要做⼀个关于收集程序崩溃信息的模块(BugReport),需要客户端程序在崩溃发⽣后将崩溃⽇志以及转储⽂件发送到后台。
2.http 格式
multipart/form-data
这⼜是⼀个常见的 POST 数据提交的⽅式。我们使⽤表单上传⽂件时,必须让 <form> 表单的enctype 等于 multipart/form-data。直接来看⼀个请求⽰例:
BASHPOST ample 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 的详细定义,请前往查看。
这种⽅式⼀般⽤来上传⽂件,各⼤服务端语⾔对它也有着良好的⽀持。
上⾯提到的这两种 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);
setoptionchar* 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";
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 != d(); 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"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());
return0;
}
点击下载:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论