文件传输之Http(winhttp库开发)
本文代码是使用winhttp库实现的。vs2022编译的。
HttpSever
注意事项:
1.post返回的数据是一你WirteFile 写入的文件的数据。
2.post会生成一个临时文件,需要看需求是否保存,本代码中保存了。
3.客户端Get请求会返回一个字符串,可以按要求修改。
sever.h
#pragma once
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <http.h>
#include <stdio.h>
#pragma comment(lib, "httpapi.lib")
//
// Macros.初始化HTTP响应体宏
//
#define INITIALIZE_HTTP_RESPONSE( resp, status, reason ) \
do \
{ \
RtlZeroMemory( (resp), sizeof(*(resp)) ); \
(resp)->StatusCode = (status); \
(resp)->pReason = (reason); \
(resp)->ReasonLength = (USHORT) strlen(reason); \
} while (FALSE)
#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue) \
do \
{ \
(Response).Headers.KnownHeaders[(HeaderId)].pRawValue = \
(RawValue);\
(Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \
(USHORT) strlen(RawValue); \
} while(FALSE)
#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb))
#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
//
// Prototypes.原型
//
DWORD DoReceiveRequests(HANDLE hReqQueue);
DWORD
SendHttpResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest,
IN USHORT StatusCode,
IN PSTR pReason,
IN PSTR pEntity
);
DWORD
SendHttpPostResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
);
sever.cpp
#include "sever.h"
#include <string>
//UTF8-->Unicode
std::wstring u8StringTouWString(const std::string& str)
{
int num = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t* wide = new wchar_t[num];
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wide, num);
std::wstring uwstr = wide;
delete[] wide;
return uwstr;
}
//Unicode-->UTF8
std::string uWStringTou8String(const std::wstring& wszString)
{
int num = WideCharToMultiByte(CP_UTF8, 0, wszString.c_str(), -1, NULL, 0, NULL, NULL);
char* wide = new char[num];
WideCharToMultiByte(CP_UTF8, 0, wszString.c_str(), -1, wide, num, NULL, NULL);
std::string str = wide;
delete[] wide;
return std::move(str);
}
/*******************************************************************++
函数说明:
他的功能是接收一个请求。
该函数调用相应的函数来处理响应。
参数:
hReqQueue - 请求队列句柄
返回值:
Success/Failure.
--*******************************************************************/
DWORD DoReceiveRequests(HANDLE hReqQueue)
{
ULONG result;
HTTP_REQUEST_ID requestId;
DWORD bytesRead;
PHTTP_REQUEST pRequest;
PCHAR pRequestBuffer;
ULONG RequestBufferLength;
//
// 分配一个2 KB缓冲区。 这个大小应该适用于大多数请求。 如果需要,
// 可以增加缓冲区大小。HTTP_REQUEST结构也需要空间。
//
RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength);
if (pRequestBuffer == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
pRequest = (PHTTP_REQUEST)pRequestBuffer;
//
// 等待一个新请求. 标记为一个NULL请求ID
//
HTTP_SET_NULL_ID(&requestId);
for (;;)
{
RtlZeroMemory(pRequest, RequestBufferLength);
result = HttpReceiveHttpRequest(
hReqQueue, // Req Queue
requestId, // Req ID
0, // Flags
pRequest, // HTTP request buffer
RequestBufferLength,// req buffer length
&bytesRead, // bytes received
NULL // LPOVERLAPPED
);
if (NO_ERROR == result)
{
//
// Worked!
//
switch (pRequest->Verb)
{
/* GET 请求处理 */
case HttpVerbGET:
wprintf(L"Got a GET request for %ws \n",
pRequest->CookedUrl.pFullUrl);
result = SendHttpResponse(
hReqQueue,
pRequest,
200,
"OK",
"Hey2! You hit the server \r\n"
);
break;
/* POST 请求处理 */
case HttpVerbPOST:
wprintf(L"Got a POST request for %ws \n",
pRequest->CookedUrl.pFullUrl);
result = SendHttpPostResponse(hReqQueue, pRequest);
break;
default:
wprintf(L"Got a unknown request for %ws \n",
pRequest->CookedUrl.pFullUrl);
result = SendHttpResponse(
hReqQueue,
pRequest,
503,
"Not Implemented",
NULL
);
break;
}
if (result != NO_ERROR)
{
break;
}
//
// 重置请求ID用于处理下一个请求.
//
HTTP_SET_NULL_ID(&requestId);
}
else if (result == ERROR_MORE_DATA)
{
//
// 输入缓冲区太小,无法容纳请求标头。增加缓冲区大小,再次调用API。
//
// 再次调用API时,通过传递RequestID来处理失败的请求。
//
// 该RequestID从旧缓冲区读取。
//
requestId = pRequest->RequestId;
//
// 释放旧的缓冲区并分配一个新的缓冲区。
//
RequestBufferLength = bytesRead;
FREE_MEM(pRequestBuffer);
pRequestBuffer = (PCHAR)ALLOC_MEM(RequestBufferLength);
if (pRequestBuffer == NULL)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
pRequest = (PHTTP_REQUEST)pRequestBuffer;
}
else if (ERROR_CONNECTION_INVALID == result &&
!HTTP_IS_NULL_ID(&requestId))
{
// 当尝试使用更多缓冲区来处理请求时,TCP连接被对方破坏
// 继续下一个请求。
HTTP_SET_NULL_ID(&requestId);
}
else
{
break;
}
}
if (pRequestBuffer)
{
FREE_MEM(pRequestBuffer);
}
return result;
}
/*******************************************************************++
函数说明:
这个函数用于发送一个HTTP响应
参数:
hReqQueue - 请求队列句柄
pRequest - 解析出的HTTP请求
StatusCode - Response状态码
pReason - Response原因短语
pEntityString - Response实体主体
返回值:
Success/Failure.
--*******************************************************************/
DWORD SendHttpResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest,
IN USHORT StatusCode,
IN PSTR pReason,
IN PSTR pEntityString
)
{
HTTP_RESPONSE response;
HTTP_DATA_CHUNK dataChunk;
DWORD result;
DWORD bytesSent;
//
// 初始化HTTP response结构体
//
INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason);
//
// 添加一个known header.
//
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html");
if (pEntityString)
{
//
// 添加一个entity chunk.
//
dataChunk.DataChunkType = HttpDataChunkFromMemory;
dataChunk.FromMemory.pBuffer = pEntityString;
dataChunk.FromMemory.BufferLength =
(ULONG)strlen(pEntityString);
response.EntityChunkCount = 1;
response.pEntityChunks = &dataChunk;
}
//
// 因为entity body在一个调用中发送,所以不需要指定Content-Length。
//
result = HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
0, // Flags
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent (OPTIONAL)
NULL, // pReserved2 (must be NULL)
0, // Reserved3 (must be 0)
NULL, // LPOVERLAPPED(OPTIONAL)
NULL // pReserved4 (must be NULL)
);
if (result != NO_ERROR)
{
wprintf(L"HttpSendHttpResponse failed with %lu \n", result);
}
return result;
}
#define MAX_ULONG_STR ((ULONG) sizeof("4294967295"))
/*******************************************************************++
函数说明:
这个函数在读取entity body后发送HTTP响应
参数:
hReqQueue - 请求队列句柄
pRequest - 解析出的HTTP request.
返回值:
Success/Failure.
--*******************************************************************/
DWORD SendHttpPostResponse(
IN HANDLE hReqQueue,
IN PHTTP_REQUEST pRequest
)
{
HTTP_RESPONSE response;
DWORD result;
DWORD bytesSent;
PWCHAR pEntityBuffer;
ULONG EntityBufferLength;
ULONG BytesRead;
ULONG TempFileBytesWritten;
HANDLE hTempFile;
TCHAR szTempName[MAX_PATH + 1];
CHAR szContentLength[MAX_ULONG_STR];
HTTP_DATA_CHUNK dataChunk;
ULONG TotalBytesRead = 0;
std::string sBody;
BytesRead = 0;
hTempFile = INVALID_HANDLE_VALUE;
//
// 为实体缓冲区分配空间。 缓冲区可按需增加。
//
EntityBufferLength = 2048;
pEntityBuffer = (PWCHAR)ALLOC_MEM(EntityBufferLength);
if (pEntityBuffer == NULL)
{
result = ERROR_NOT_ENOUGH_MEMORY;
wprintf(L"Insufficient resources \n");
goto Done;
}
//
// 初始化HTTP response结构体.
//
INITIALIZE_HTTP_RESPONSE(&response, 200, "OK");
//
// 对于POST,从客户端回显实体
//
// 注意: 如果HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY标识通过HttpReceiveHttpRequest()
// 传递,则entity将是HTTP_REQUEST的一部分(使用pEntityChunks字段).因为此标识
// 未被传递,则entity不在HTTP_REQUEST中.
//
if (pRequest->Flags & HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS)
{
// 实体主体通过多个调用发送. 收集这些在一个文件并回发.创建一个临时文件
//
GetCurrentDirectory(MAX_PATH,szTempName);
if (GetTempFileName(
szTempName,
L"New",
0,
szTempName
) == 0)
{
result = GetLastError();
wprintf(L"GetTempFileName failed with %lu \n", result);
goto Done;
}
hTempFile = CreateFile(
szTempName,
GENERIC_READ | GENERIC_WRITE,
0, // Do not share.
NULL, // No security descriptor.
CREATE_ALWAYS, // Overrwrite existing.
FILE_ATTRIBUTE_NORMAL, // Normal file.
NULL
);
if (hTempFile == INVALID_HANDLE_VALUE)
{
result = GetLastError();
wprintf(L"Cannot create temporary file. Error %lu \n",
result);
goto Done;
}
do
{
//
// 从请求中读取entity chunk.
//
BytesRead = 0;
result = HttpReceiveRequestEntityBody(
hReqQueue,
pRequest->RequestId,
0,
pEntityBuffer,
EntityBufferLength,
&BytesRead,
NULL
);
switch (result)
{
case NO_ERROR:
if (BytesRead != 0)
{
sBody = uWStringTou8String(pEntityBuffer);
TotalBytesRead += sBody.size();
WriteFile(
hTempFile,
sBody.c_str(),
sBody.size(),
&TempFileBytesWritten,
NULL
);
}
break;
case ERROR_HANDLE_EOF:
//
// The last request entity body has been read.
// Send back a response.
//
// To illustrate entity sends via
// HttpSendResponseEntityBody, the response will
// be sent over multiple calls. To do this,
// pass the HTTP_SEND_RESPONSE_FLAG_MORE_DATA
// flag.
if (BytesRead != 0)
{
sBody = uWStringTou8String(pEntityBuffer);
TotalBytesRead += sBody.size();
WriteFile(
hTempFile,
sBody.c_str(),
sBody.size(),
&TempFileBytesWritten,
NULL
);
}
//
// Because the response is sent over multiple
// API calls, add a content-length.
//
// Alternatively, the response could have been
// sent using chunked transfer encoding, by
// passimg "Transfer-Encoding: Chunked".
//
// NOTE: Because the TotalBytesread in a ULONG
// are accumulated, this will not work
// for entity bodies larger than 4 GB.
// For support of large entity bodies,
// use a ULONGLONG.
//
sprintf_s(szContentLength, MAX_ULONG_STR, "%lu", TotalBytesRead);
ADD_KNOWN_HEADER(
response,
HttpHeaderContentLength,
szContentLength
);
result =
HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
HTTP_SEND_RESPONSE_FLAG_MORE_DATA,
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent-optional
NULL, // pReserved2
0, // Reserved3
NULL, // LPOVERLAPPED
NULL // pReserved4
);
if (result != NO_ERROR)
{
wprintf(
L"HttpSendHttpResponse failed with %lu \n",
result
);
goto Done;
}
//
// Send entity body from a file handle.
//
dataChunk.DataChunkType =
HttpDataChunkFromFileHandle;
dataChunk.FromFileHandle.
ByteRange.StartingOffset.QuadPart = 0;
dataChunk.FromFileHandle.
ByteRange.Length.QuadPart =
HTTP_BYTE_RANGE_TO_EOF;
dataChunk.FromFileHandle.FileHandle = hTempFile;
result = HttpSendResponseEntityBody(
hReqQueue,
pRequest->RequestId,
0, // This is the last send.
1, // Entity Chunk Count.
&dataChunk,
NULL,
NULL,
0,
NULL,
NULL
);
if (result != NO_ERROR)
{
wprintf(
L"HttpSendResponseEntityBody failed %lu\n",
result
);
}
goto Done;
break;
default:
wprintf(
L"HttpReceiveRequestEntityBody failed with %lu \n",
result);
goto Done;
}
} while (TRUE);
}
else
{
// 此请求没有实体主体。
//
result = HttpSendHttpResponse(
hReqQueue, // ReqQueueHandle
pRequest->RequestId, // Request ID
0,
&response, // HTTP response
NULL, // pReserved1
&bytesSent, // bytes sent (optional)
NULL, // pReserved2
0, // Reserved3
NULL, // LPOVERLAPPED
NULL // pReserved4
);
if (result != NO_ERROR)
{
wprintf(L"HttpSendHttpResponse failed with %lu \n",
result);
}
}
Done:
if (pEntityBuffer)
{
FREE_MEM(pEntityBuffer);
}
if (INVALID_HANDLE_VALUE != hTempFile)
{
CloseHandle(hTempFile);
//看需求更改 如果需要保存文件不用删除
//DeleteFile(szTempName);
}
return result;
}
调用示例:
1.需要监听多个URL端口的自己按需求添加。
2.传入的参数:args[2]=http://*:8080/;
#include "sever.h"
/*******************************************************************++
函数说明:
main函数
参数:
argc - 命令行参数个数.
argv - 命令行参数.
返回值:
Success/Failure
--*******************************************************************/
int __cdecl wmain(
int argc,
wchar_t* argv[]
)
{
ULONG retCode;
HANDLE hReqQueue = NULL;
int UrlAdded = 0;
HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;
if (argc < 2)
{
wprintf(L"%ws: <Url1> [Url2] ... \n", argv[0]);
return -1;
}
//
// 初始化HTTP Server APIs
//
retCode = HttpInitialize(
HttpApiVersion,
HTTP_INITIALIZE_SERVER, // Flags
NULL // Reserved
);
if (retCode != NO_ERROR)
{
wprintf(L"HttpInitialize failed with %lu \n", retCode);
return retCode;
}
//
// 创建请求队列句柄
//
retCode = HttpCreateHttpHandle(
&hReqQueue, // Req Queue
0 // Reserved
);
if (retCode != NO_ERROR)
{
wprintf(L"HttpCreateHttpHandle failed with %lu \n", retCode);
goto CleanUp;
}
//
// 命令行参数指定要监听的URI。为每个URI调用HttpAddUrl。
//
// URI是一个完全合格的URI,必须包含终止字符(/)
//
for (int i = 1; i < argc; i++)
{
wprintf(L"listening for requests on the following url: %s\n", argv[i]);
retCode = HttpAddUrl(
hReqQueue, // Req Queue
argv[i], // Fully qualified URL
NULL // Reserved
);
if (retCode != NO_ERROR)
{
wprintf(L"HttpAddUrl failed with %lu \n", retCode);
goto CleanUp;
}
else
{
//
// Track the currently added URLs.
//
UrlAdded++;
}
}
DoReceiveRequests(hReqQueue);
CleanUp:
//
// 对所有添加的URI调用HttpRemoveUrl.
//
for (int i = 1; i <= UrlAdded; i++)
{
HttpRemoveUrl(
hReqQueue, // Req Queue
argv[i] // Fully qualified URL
);
}
//
// 关闭请求队列句柄.
//
if (hReqQueue)
{
CloseHandle(hReqQueue);
}
//
// 调用HttpTerminate.
//
HttpTerminate(HTTP_INITIALIZE_SERVER, NULL);
return retCode;
}
HttpClient
http.h
#pragma once
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <http.h>
#include <iostream>
#include <Windows.h>
#include <WinInet.h>
#include <filesystem> // C++17 标准库
#pragma comment(lib, "WinInet.lib")
using namespace std;
namespace fs = std::filesystem;
class Chttp
{
// 从响应信息头信息中获取数据内容长度大小
BOOL GetContentLength(wchar_t* pResponseHeader, DWORD* pdwContentLength)
{
int i = 0;
char szContentLength[MAX_PATH] = { 0 };
DWORD dwContentLength = 0;
wchar_t szSubStr[] = L"Content-Length: ";
RtlZeroMemory(szContentLength, MAX_PATH);
// 在传入字符串中查找子串
wchar_t* p = wcsstr(pResponseHeader, szSubStr);
if (NULL == p)
{
return FALSE;
}
p = p + lstrlen(szSubStr);
// 如果找到了就提取出里面的纯数字
while (('0' <= *p) && ('9' >= *p))
{
szContentLength[i] = *p;
p++;
i++;
}
// 字符串转数字
dwContentLength = atoi(szContentLength);
*pdwContentLength = dwContentLength;
return TRUE;
}
// 切割路径
BOOL UrlCrack(wchar_t* pszUrl, wchar_t* pszScheme, wchar_t* pszHostName, wchar_t* pszUserName, wchar_t* pszPassword, wchar_t* pszUrlPath, wchar_t* pszExtraInfo, WORD* nPort,DWORD dwBufferSize)
{
BOOL bRet = FALSE;
URL_COMPONENTS uc = { 0 };
// 初始化变量中的内容
RtlZeroMemory(&uc, sizeof(uc));
RtlZeroMemory(pszScheme, dwBufferSize);
RtlZeroMemory(pszHostName, dwBufferSize);
RtlZeroMemory(pszUserName, dwBufferSize);
RtlZeroMemory(pszPassword, dwBufferSize);
RtlZeroMemory(pszUrlPath, dwBufferSize);
RtlZeroMemory(pszExtraInfo, dwBufferSize);
// 将长度填充到结构中
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = dwBufferSize - 1;
uc.dwHostNameLength = dwBufferSize - 1;
uc.dwUserNameLength = dwBufferSize - 1;
uc.dwPasswordLength = dwBufferSize - 1;
uc.dwUrlPathLength = dwBufferSize - 1;
uc.dwExtraInfoLength = dwBufferSize - 1;
uc.lpszScheme = pszScheme;
uc.lpszHostName = pszHostName;
uc.lpszUserName = pszUserName;
uc.lpszPassword = pszPassword;
uc.lpszUrlPath = pszUrlPath;
uc.lpszExtraInfo = pszExtraInfo;
// 分解URL地址
bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);
if (FALSE == bRet)
{
return bRet;
}
*nPort = uc.nPort;
return bRet;
}
public:
// 数据上传
BOOL HttpUpload(wchar_t* pszUploadUrl, WCHAR* pUploadData, DWORD dwUploadDataSize)
{
// 初始化变量中的内容
wchar_t szScheme[MAX_PATH] = { 0 };
wchar_t szHostName[MAX_PATH] = { 0 };
wchar_t szUserName[MAX_PATH] = { 0 };
wchar_t szPassword[MAX_PATH] = { 0 };
wchar_t szUrlPath[MAX_PATH] = { 0 };
wchar_t szExtraInfo[MAX_PATH] = { 0 };
// 将长度填充到结构中
RtlZeroMemory(szScheme, MAX_PATH);
RtlZeroMemory(szHostName, MAX_PATH);
RtlZeroMemory(szUserName, MAX_PATH);
RtlZeroMemory(szPassword, MAX_PATH);
RtlZeroMemory(szUrlPath, MAX_PATH);
RtlZeroMemory(szExtraInfo, MAX_PATH);
WORD port = 0;
// 分解URL地址
if (FALSE == UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, &port,MAX_PATH))
{
return FALSE;
}
// 数据上传
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
DWORD dwOpenRequestFlags = 0;
BOOL bRet = FALSE;
DWORD dwRet = 0;
wchar_t* pResponseHeaderIInfo = NULL;
DWORD dwResponseHeaderIInfoSize = 2048;
WORD* pBuf = NULL;
DWORD dwBufSize = 64 * 1024;
wchar_t* pResponseBodyData = NULL;
DWORD dwResponseBodyDataSize = 0;
DWORD dwOffset = 0;
DWORD dwPostDataSize = dwUploadDataSize;
INTERNET_BUFFERS internetBuffers = { 0 };
WCHAR buffer[0x40]{};
do
{
// 建立会话
hInternet = InternetOpen(L"WinInetPost/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (NULL == hInternet)
{
wsprintf(buffer, L"InternetOpen error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 建立连接
hConnect = InternetConnect(hInternet, szHostName, port, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
if (NULL == hConnect)
{
wsprintf(buffer, L"InternetConnect error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 打开并发送HTTP请求
dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD;
if (0 < lstrlen(szExtraInfo))
{
lstrcat(szUrlPath, szExtraInfo);
}
// 使用POST请求
hRequest = HttpOpenRequest(hConnect, L"POST", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
wsprintf(buffer, L"HttpOpenRequest error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 告诉服务器传输数据的总大小
RtlZeroMemory(&internetBuffers, sizeof(internetBuffers));
internetBuffers.dwStructSize = sizeof(internetBuffers);
internetBuffers.dwBufferTotal = dwPostDataSize;
bRet = HttpSendRequestEx(hRequest, &internetBuffers, NULL, 0, 0);
if (FALSE == bRet)
{
wsprintf(buffer, L"HttpSendRequestEx error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 发送数据
bRet = InternetWriteFile(hRequest, pUploadData, dwUploadDataSize, &dwRet);
if (FALSE == bRet)
{
wsprintf(buffer, L"InternetWriteFile error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 发送完毕
bRet = HttpEndRequest(hRequest, NULL, 0, 0);
if (FALSE == bRet)
{
wsprintf(buffer, L"HttpEndRequest error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 接收响应报文
pResponseHeaderIInfo = new wchar_t[dwResponseHeaderIInfoSize];
if (NULL == pResponseHeaderIInfo)
{
break;
}
RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);
bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
if (FALSE == bRet)
{
wsprintf(buffer, L"HttpQueryInfo error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 获取数据长度
bRet = GetContentLength(pResponseHeaderIInfo, &dwResponseBodyDataSize);
if (FALSE == bRet)
{
break;
}
// 输出响应头
std::wcout << pResponseHeaderIInfo << std::endl;
// 接收报文主体内容(Get Response Body)
pBuf = new WORD[dwBufSize];
if (NULL == pBuf)
{
break;
}
pResponseBodyData = new wchar_t[dwResponseBodyDataSize+1];
if (NULL == pResponseBodyData)
{
break;
}
RtlZeroMemory(pResponseBodyData, dwResponseBodyDataSize+2);
do
{
// 循环读取数据并填充到缓冲区内
RtlZeroMemory(pBuf, dwBufSize);
bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
if (FALSE == bRet)
{
break;
}
RtlCopyMemory((pResponseBodyData + dwOffset), pBuf, dwRet);
dwOffset = dwOffset + dwRet;
} while (dwResponseBodyDataSize > dwOffset);
} while (FALSE);
if (dwOffset)
{
std::cout << pResponseBodyData << std::endl;
}
else
{
wprintf(L"ResponseBodyData is zero");
}
// 关闭释放
if (NULL != pResponseBodyData)
{
delete[]pResponseBodyData;
pResponseBodyData = NULL;
}
if (NULL != pBuf)
{
delete[]pBuf;
pBuf = NULL;
}
if (NULL != pResponseHeaderIInfo)
{
delete[]pResponseHeaderIInfo;
pResponseHeaderIInfo = NULL;
}
if (NULL != hRequest)
{
InternetCloseHandle(hRequest);
hRequest = NULL;
}
if (NULL != hConnect)
{
InternetCloseHandle(hConnect);
hConnect = NULL;
}
if (NULL != hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
return bRet;
}
// 数据下载
BOOL HttpDownload(wchar_t* pszDownloadUrl, WORD** ppDownloadData, DWORD* pdwDownloadDataSize)
{
// 定义HTTP子变量
wchar_t szScheme[MAX_PATH] = { 0 };
wchar_t szHostName[MAX_PATH] = { 0 };
wchar_t szUserName[MAX_PATH] = { 0 };
wchar_t szPassword[MAX_PATH] = { 0 };
wchar_t szUrlPath[MAX_PATH] = { 0 };
wchar_t szExtraInfo[MAX_PATH] = { 0 };
WORD port = 0;
// 填充为空
RtlZeroMemory(szScheme, MAX_PATH);
RtlZeroMemory(szHostName, MAX_PATH);
RtlZeroMemory(szUserName, MAX_PATH);
RtlZeroMemory(szPassword, MAX_PATH);
RtlZeroMemory(szUrlPath, MAX_PATH);
RtlZeroMemory(szExtraInfo, MAX_PATH);
// 拆解URL地址
if (FALSE == UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, &port, MAX_PATH))
{
return FALSE;
}
// 数据下载
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
DWORD dwOpenRequestFlags = 0;
BOOL bRet = FALSE;
wchar_t* pResponseHeaderIInfo = NULL;
DWORD dwResponseHeaderIInfoSize = 2048;
WORD* pBuf = NULL;
DWORD dwBufSize = 64 * 1024;
WORD* pDownloadData = NULL;
DWORD dwDownloadDataSize = 0;
DWORD dwRet = 0;
DWORD dwOffset = 0;
WCHAR buffer[0x40]{};
do
{
// 建立会话
hInternet = InternetOpen(L"WinInetGet/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (NULL == hInternet)
{
wsprintf(buffer, L"InternetOpen error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 建立连接
hConnect = InternetConnect(hInternet, szHostName, port, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
if (NULL == hConnect)
{
wsprintf(buffer, L"InternetConnect error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 打开并发送HTTP请求
dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
INTERNET_FLAG_KEEP_CONNECTION |
INTERNET_FLAG_NO_AUTH |
INTERNET_FLAG_NO_COOKIES |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD;
if (0 < lstrlen(szExtraInfo))
{
lstrcat(szUrlPath, szExtraInfo);
}
// 以GET模式打开请求
hRequest = HttpOpenRequest(hConnect, L"GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
if (NULL == hRequest)
{
wsprintf(buffer, L"HttpOpenRequest error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 发送请求
bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);
if (FALSE == bRet)
{
wsprintf(buffer, L"HttpSendRequest error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 接收响应的报文信息头(Get Response Header)
pResponseHeaderIInfo = new wchar_t[dwResponseHeaderIInfoSize];
if (NULL == pResponseHeaderIInfo)
{
break;
}
RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);
// 查询HTTP请求头
bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
if (FALSE == bRet)
{
wsprintf(buffer, L"HttpQueryInfo error code:%d", GetLastError());
wprintf(buffer);
break;
}
// 从字符串中 "Content-Length: " 网页获取数据长度
bRet = GetContentLength(pResponseHeaderIInfo, &dwDownloadDataSize);
// 输出完整响应头
std::wcout << pResponseHeaderIInfo << std::endl;
if (FALSE == bRet)
{
break;
}
// 接收报文主体内容(Get Response Body)
pBuf = new WORD[dwBufSize];
if (NULL == pBuf)
{
break;
}
pDownloadData = new WORD[dwDownloadDataSize];
if (NULL == pDownloadData)
{
break;
}
RtlZeroMemory(pDownloadData, dwDownloadDataSize);
do
{
RtlZeroMemory(pBuf, dwBufSize);
// 循环读入数据并保存在变量中
bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
if (FALSE == bRet)
{
wsprintf(buffer, L"InternetReadFile error code:%d", GetLastError());
wprintf(buffer);
break;
}
RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwRet);
dwOffset = dwOffset + dwRet;
} while (dwDownloadDataSize > dwOffset);
// 返回数据
*ppDownloadData = pDownloadData;
*pdwDownloadDataSize = dwDownloadDataSize;
} while (FALSE);
// 关闭并释放资源
if (NULL != pBuf)
{
delete[]pBuf;
pBuf = NULL;
}
if (NULL != pResponseHeaderIInfo)
{
delete[]pResponseHeaderIInfo;
pResponseHeaderIInfo = NULL;
}
if (NULL != hRequest)
{
InternetCloseHandle(hRequest);
hRequest = NULL;
}
if (NULL != hConnect)
{
InternetCloseHandle(hConnect);
hConnect = NULL;
}
if (NULL != hInternet)
{
InternetCloseHandle(hInternet);
hInternet = NULL;
}
return bRet;
}
// 创建并保存文件
BOOL SaveToFile(wchar_t* pszFileName, WORD* pData, DWORD dwDataSize)
{
fs::path fileDir = fs::path(pszFileName).parent_path();
if (!fs::exists(fileDir)) {
if (!fs::create_directories(fileDir)) {
std::cerr << "Failed to create directory: " << fileDir << std::endl;
return false;
}
}
// 创建空文件
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
return FALSE;
}
DWORD dwRet = 0;
// 写出数据到文件
WriteFile(hFile, pData, dwDataSize, &dwRet, NULL);
// 关闭句柄保存文件
CloseHandle(hFile);
return TRUE;
}
//UTF8-->Unicode
std::wstring u8StringTouWString(const std::string& str)
{
int num = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t* wide = new wchar_t[num];
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, wide, num);
std::wstring uwstr = wide;
delete[] wide;
return uwstr;
}
//Unicode-->UTF8
std::string uWStringTou8String(const std::wstring& wszString)
{
int num = WideCharToMultiByte(CP_UTF8, 0, wszString.c_str(), -1, NULL, 0, NULL, NULL);
char* wide = new char[num];
WideCharToMultiByte(CP_UTF8, 0, wszString.c_str(), -1, wide, num, NULL, NULL);
std::string str = wide;
delete[] wide;
return str;
}
};
示例:
#include "chttp.h"
int main(int argc, char* argv[])
{
Chttp chttp;
// 设置需要下载的地址
wchar_t szHttpDownloadUrl[] = L"http://127.0.0.1:8080";
WORD* pHttpDownloadData = NULL;
DWORD dwHttpDownloadDataSize = 0;
// HTTP下载
if (TRUE == chttp.HttpDownload(szHttpDownloadUrl, &pHttpDownloadData, &dwHttpDownloadDataSize))
{
std::cout << "Save File Len: " << dwHttpDownloadDataSize << " byte" << std::endl;
}
// 将下载数据保存成文件
chttp.SaveToFile(L"C:\\test\\123.txt", pHttpDownloadData, dwHttpDownloadDataSize);
// 释放内存
delete[]pHttpDownloadData;
pHttpDownloadData = NULL;
system("pause");
// 设置上传接口地址
wchar_t szHttpUploadUrl[] = L"http://127.0.0.1:8080";
// 被上传文件绝对路径
wchar_t szHttpUploadFileName[] = L"C:\\test\\123.txt";
char* pHttpUploadData = NULL;
DWORD dwHttpUploadDataSize = 0;
DWORD dwRet = 0;
std::wstring data;
// 打开文件
HANDLE hFile = CreateFile(szHttpUploadFileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE, NULL);
// 获取文件大小
dwHttpUploadDataSize = GetFileSize(hFile, NULL);
dwHttpUploadDataSize += 1;
// 读取文件数据
pHttpUploadData = new char[dwHttpUploadDataSize];
memset(pHttpUploadData, 0, dwHttpUploadDataSize);
ReadFile(hFile, pHttpUploadData, dwHttpUploadDataSize, &dwRet, NULL);
dwHttpUploadDataSize = dwRet;
data = chttp.u8StringTouWString(pHttpUploadData);
// 上传数据
if (FALSE == chttp.HttpUpload(szHttpUploadUrl, (WCHAR*)data.c_str(), dwHttpUploadDataSize))
{
return 0;
}
// 释放内存
delete[]pHttpUploadData;
pHttpUploadData = NULL;
CloseHandle(hFile);
system("pause");
return 0;
}