文件传输之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;
}