C/C++ 通过HTTP实现文件上传下载

WinInet(Windows Internet)是 Microsoft Windows 操作系统中的一个 API 集,用于提供对 Internet 相关功能的支持。它包括了一系列的函数,使得 Windows 应用程序能够进行网络通信、处理 HTTP 请求、FTP 操作等。WinInet 提供了一套完整的网络通信工具,使得开发者能够轻松地构建支持网络功能的应用程序,涵盖了从简单的 HTTP 请求到复杂的文件传输等多种网络操作。

分解URL地址

InternetCrackUrl 函数可实现对URL字符串进行解析,提取其中的协议、主机名、端口、路径和其他信息,并将这些信息存储在 URL_COMPONENTS 结构中,方便后续的网络操作,该函数是Windows下默认提供的,函数与依赖结果如下所示;

函数原型

BOOL InternetCrackUrl(
  LPCTSTR      lpszUrl,
  DWORD        dwUrlLength,
  DWORD        dwFlags,
  LPURL_COMPONENTS lpUrlComponents
);

参数说明

  • lpszUrl:指定待解析的 URL 字符串。
  • dwUrlLength:指定 URL 字符串的长度。
  • dwFlags:指定解析 URL 的标志,可以是以下值之一:
    • ICU_DECODE:对 URL 进行解码。
    • ICU_ESCAPE:对 URL 进行转义。
  • lpUrlComponents:一个指向 URL_COMPONENTS 结构的指针,用于存储解析后的各个部分信息。

URL_COMPONENTS结构

typedef struct {
  DWORD dwStructSize;
  LPTSTR lpszScheme;
  DWORD dwSchemeLength;
  INTERNET_SCHEME nScheme;
  LPTSTR lpszHostName;
  DWORD dwHostNameLength;
  INTERNET_PORT nPort;
  LPTSTR lpszUserName;
  DWORD dwUserNameLength;
  LPTSTR lpszPassword;
  DWORD dwPasswordLength;
  LPTSTR lpszUrlPath;
  DWORD dwUrlPathLength;
  LPTSTR lpszExtraInfo;
  DWORD dwExtraInfoLength;
} URL_COMPONENTS, *LPURL_COMPONENTS;

返回值

如果函数成功,返回 TRUE,并在 lpUrlComponents 结构中存储解析后的信息;如果失败,返回 FALSE。在失败时,可以调用 GetLastError 函数获取详细的错误信息。

函数调用

#include <iostream>
#include <Windows.h>
#include <WinInet.h>

#pragma comment(lib, "WinInet.lib")

using namespace std;

BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, 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;
	}
	return bRet;
}

int main(int argc, char* argv[])
{
	char szHttpDownloadUrl[] = "http://www.lyshark.com/index.php&username=lyshark&password=123";

	// 对应的变量
	char szScheme[MAX_PATH] = { 0 };
	char szHostName[MAX_PATH] = { 0 };
	char szUserName[MAX_PATH] = { 0 };
	char szPassword[MAX_PATH] = { 0 };
	char szUrlPath[MAX_PATH] = { 0 };
	char szExtraInfo[MAX_PATH] = { 0 };

	// 初始化用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(szHttpDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
	{
		return FALSE;
	}

	std::cout << szScheme << std::endl;
	std::cout << szHostName << std::endl;
	std::cout << szUserName << std::endl;
	std::cout << szPassword << std::endl;
	std::cout << szUrlPath << std::endl;
	std::cout << szExtraInfo << std::endl;

	system("pause");
	return 0;
}

运行代码输出特定网址的每个部分,如下图所示;

下载页面内容

InternetOpen

用于初始化 WinINet 函数的使用。以下是该函数的原型:

HINTERNET InternetOpen(
  LPCWSTR lpszAgent,
  DWORD   dwAccessType,
  LPCWSTR lpszProxyName,
  LPCWSTR lpszProxyBypass,
  DWORD   dwFlags
);

参数说明:

  • lpszAgent: 指定应用程序的名称,用于标识调用 InternetOpen 的应用程序。
  • dwAccessType: 指定访问类型,可以是 INTERNET_OPEN_TYPE_DIRECTINTERNET_OPEN_TYPE_PROXYINTERNET_OPEN_TYPE_PRECONFIG 中的一个。
  • lpszProxyName: 如果 dwAccessTypeINTERNET_OPEN_TYPE_PROXY,则指定代理服务器的名称。否则,可以设为 NULL
  • lpszProxyBypass: 如果 dwAccessTypeINTERNET_OPEN_TYPE_PROXY,则指定绕过代理服务器的地址。否则,可以设为 NULL
  • dwFlags: 一些标志,可以用来指定额外的行为,如 INTERNET_FLAG_ASYNC 用于异步操作。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,用于后续的 WinINet 操作。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

InternetConnect

用于建立到远程服务器的连接。以下是该函数的原型:

HINTERNET InternetConnect(
  HINTERNET     hInternet,
  LPCWSTR       lpszServerName,
  INTERNET_PORT nServerPort,
  LPCWSTR       lpszUserName,
  LPCWSTR       lpszPassword,
  DWORD         dwService,
  DWORD         dwFlags,
  DWORD_PTR     dwContext
);

参数说明:

  • hInternet: 调用 InternetOpen 返回的句柄,表示连接的上下文。
  • lpszServerName: 要连接的服务器的名称或 IP 地址。
  • nServerPort: 服务器的端口号。
  • lpszUserName: 连接服务器时要使用的用户名,可以为 NULL
  • lpszPassword: 连接服务器时要使用的密码,可以为 NULL
  • dwService: 指定服务类型,可以是 INTERNET_SERVICE_FTPINTERNET_SERVICE_HTTP 或其他服务类型。
  • dwFlags: 一些标志,用于指定连接的属性,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示连接的上下文。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

InternetConnect 用于建立连接后,可以使用返回的句柄执行相关的协议操作,如 FTP 或 HTTP 操作。使用完连接后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpOpenRequest

它是在使用 WinINet 库进行 HTTP 操作时的一部分。以下是该函数的原型:

HINTERNET HttpOpenRequest(
  HINTERNET hConnect,
  LPCWSTR   lpszVerb,
  LPCWSTR   lpszObjectName,
  LPCWSTR   lpszVersion,
  LPCWSTR   lpszReferrer,
  LPCWSTR   *lplpszAcceptTypes,
  DWORD     dwFlags,
  DWORD_PTR dwContext
);

参数说明:

  • hConnect: 调用 InternetConnect 返回的连接句柄,表示请求的上下文。
  • lpszVerb: HTTP 请求方法,如 “GET”、“POST” 等。
  • lpszObjectName: 请求的对象名,通常是 URL 的路径部分。
  • lpszVersion: HTTP 协议版本,通常是 “HTTP/1.1”。
  • lpszReferrer: 引用的来源,可以为 NULL
  • lplpszAcceptTypes: 指定可接受的媒体类型,可以为 NULL
  • dwFlags: 一些标志,用于指定请求的属性,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,将返回一个类型为 HINTERNET 的句柄,表示打开的 HTTP 请求。如果函数调用失败,返回 NULL。可以使用 GetLastError 函数获取详细的错误信息。

一旦打开了 HTTP 请求,可以使用返回的句柄执行发送请求、接收响应等操作。使用完请求后,同样需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpSendRequest

用于发送 HTTP 请求的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

BOOL HttpSendRequest(
  HINTERNET hRequest,
  LPCWSTR   lpszHeaders,
  DWORD     dwHeadersLength,
  LPVOID    lpOptional,
  DWORD     dwOptionalLength
);

参数说明:

  • hRequest: 调用 HttpOpenRequest 返回的 HTTP 请求句柄,表示要发送请求的上下文。
  • lpszHeaders: 包含请求头信息的字符串,可以为 NULL
  • dwHeadersLength: 请求头的长度,如果 lpszHeadersNULL,则可以为零。
  • lpOptional: 包含请求的可选数据的缓冲区,可以为 NULL
  • dwOptionalLength: 可选数据的长度,如果 lpOptionalNULL,则可以为零。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpSendRequest 用于实际发送 HTTP 请求。在调用此函数之后,可以使用其他 WinINet 函数来读取服务器的响应。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpQueryInfo

用于检索有关 HTTP 请求或响应的信息的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

BOOL HttpQueryInfo(
  HINTERNET hRequest,
  DWORD     dwInfoLevel,
  LPVOID    lpBuffer,
  LPDWORD   lpdwBufferLength,
  LPDWORD   lpdwIndex
);

参数说明:

  • hRequest: 调用 HttpOpenRequest 返回的 HTTP 请求句柄,表示要查询信息的上下文。
  • dwInfoLevel: 指定要检索的信息类型,可以是预定义的常量,如 HTTP_QUERY_STATUS_CODEHTTP_QUERY_CONTENT_TYPE 等。
  • lpBuffer: 用于接收检索到的信息的缓冲区。
  • lpdwBufferLength: 指向一个变量,表示 lpBuffer 缓冲区的大小。在调用函数前,应该将该变量设置为 lpBuffer 缓冲区的大小。在调用函数后,该变量将包含实际写入缓冲区的字节数。
  • lpdwIndex: 如果请求返回多个值,可以使用此参数指定要检索的值的索引。对于单值的信息,可以将其设置为 NULL

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpQueryInfo 用于获取与 HTTP 请求或响应相关的信息,如状态码、内容类型等。注意,在调用此函数之前,通常需要先调用 HttpSendRequest 发送请求。同样,使用完请求后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

InternetReadFile

用于从指定的句柄读取数据的函数,通常在使用 WinINet 库进行网络操作时调用。以下是该函数的原型:

BOOL InternetReadFile(
  HINTERNET hFile,
  LPVOID    lpBuffer,
  DWORD     dwNumberOfBytesToRead,
  LPDWORD   lpdwNumberOfBytesRead
);

参数说明:

  • hFile: 调用 HttpOpenRequestFtpOpenFile 返回的句柄,表示要读取数据的上下文。
  • lpBuffer: 用于接收读取到的数据的缓冲区。
  • dwNumberOfBytesToRead: 指定要读取的字节数。
  • lpdwNumberOfBytesRead: 指向一个变量,表示 lpBuffer 缓冲区中实际读取的字节数。在调用函数前,应该将该变量设置为 lpBuffer 缓冲区的大小。在调用函数后,该变量将包含实际读取的字节数。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

InternetReadFile 用于从网络资源中读取数据,如从 HTTP 请求的响应中读取内容。在调用此函数之前,通常需要先调用其他相关的函数,如 HttpOpenRequestHttpSendRequestHttpQueryInfo。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

下载页面的完整代码是这样的,如下所示;

#include <iostream>
#include <Windows.h>
#include <WinInet.h>

#pragma comment(lib, "WinInet.lib")

using namespace std;

BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, 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;
	}
	return bRet;
}

// 从响应信息头信息中获取数据内容长度大小
BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)
{
	int i = 0;
	char szContentLength[MAX_PATH] = { 0 };
	DWORD dwContentLength = 0;
	char szSubStr[] = "Content-Length: ";
	RtlZeroMemory(szContentLength, MAX_PATH);

	// 在传入字符串中查找子串
	char* p = strstr(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 HttpDownload(char* pszDownloadUrl, BYTE** ppDownloadData, DWORD* pdwDownloadDataSize)
{
	// 定义HTTP子变量
	char szScheme[MAX_PATH] = { 0 };
	char szHostName[MAX_PATH] = { 0 };
	char szUserName[MAX_PATH] = { 0 };
	char szPassword[MAX_PATH] = { 0 };
	char szUrlPath[MAX_PATH] = { 0 };
	char 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);

	// 拆解URL地址
	if (FALSE == UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
	{
		return FALSE;
	}

	// 数据下载
	HINTERNET hInternet = NULL;
	HINTERNET hConnect = NULL;
	HINTERNET hRequest = NULL;
	DWORD dwOpenRequestFlags = 0;
	BOOL bRet = FALSE;
	unsigned char* pResponseHeaderIInfo = NULL;
	DWORD dwResponseHeaderIInfoSize = 2048;
	BYTE* pBuf = NULL;
	DWORD dwBufSize = 64 * 1024;
	BYTE* pDownloadData = NULL;
	DWORD dwDownloadDataSize = 0;
	DWORD dwRet = 0;
	DWORD dwOffset = 0;

	do
	{
		// 建立会话
		hInternet = InternetOpen("WinInetGet/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
		if (NULL == hInternet)
		{
			break;
		}

		// 建立连接
		hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
		if (NULL == hConnect)
		{
			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, "GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
		if (NULL == hRequest)
		{
			break;
		}

		// 发送请求
		bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);
		if (FALSE == bRet)
		{
			break;
		}
		// 接收响应的报文信息头(Get Response Header)
		pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];
		if (NULL == pResponseHeaderIInfo)
		{
			break;
		}
		RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);

		// 查询HTTP请求头
		bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
		if (FALSE == bRet)
		{
			break;
		}

		// 从字符串中 "Content-Length: " 网页获取数据长度
		bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwDownloadDataSize);

		// 输出完整响应头
		std::cout << pResponseHeaderIInfo << std::endl;
		if (FALSE == bRet)
		{
			break;
		}
		// 接收报文主体内容(Get Response Body)
		pBuf = new BYTE[dwBufSize];
		if (NULL == pBuf)
		{
			break;
		}
		pDownloadData = new BYTE[dwDownloadDataSize];
		if (NULL == pDownloadData)
		{
			break;
		}
		RtlZeroMemory(pDownloadData, dwDownloadDataSize);
		do
		{
			RtlZeroMemory(pBuf, dwBufSize);

			// 循环读入数据并保存在变量中
			bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);
			if (FALSE == bRet)
			{
				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(char* pszFileName, BYTE* pData, DWORD dwDataSize)
{
	// 创建空文件
	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;
}

使用时调用HttpDownload实现数据下载,下载后的文件会保存在pHttpDownloadData中,此时直接调用SaveToFile将其保存在文件中即可;

int main(int argc, char* argv[])
{
	// 设置需要下载的地址
	char szHttpDownloadUrl[] = "http://www.lyshark.com/index.html";
	BYTE* pHttpDownloadData = NULL;
	DWORD dwHttpDownloadDataSize = 0;

	// HTTP下载 
	if (TRUE == HttpDownload(szHttpDownloadUrl, &pHttpDownloadData, &dwHttpDownloadDataSize))
	{
		std::cout << "已保存文件,长度: " << dwHttpDownloadDataSize << " 字节"<< std::endl;
	}

	// 将下载数据保存成文件
	SaveToFile((char *)"d://index.html", pHttpDownloadData, dwHttpDownloadDataSize);

	// 释放内存
	delete[]pHttpDownloadData;
	pHttpDownloadData = NULL;

	system("pause");
	return 0;
}

运行后则可一输出响应头Content-Length:完整参数以及输出的字节数,如下图所示;

上传文件内容

服务端,首先需要实现一个简单的上传接收功能,这里使用flask框架实现,通过执行pip install flask命令安装这个库,安装成功以后手动保存为main.py文件,上传文件是只需要向http://127.0.0.1/upload?file=推送数据即可,代码如下;

from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload_file():
    file = request.args.get('file')
    
    if not file:
        return "上传错误,文件名未指定"

    try:
        with open(file, 'wb') as uploaded_file:
            uploaded_file.write(request.get_data())

        if os.path.exists(file):
            return "上传成功"
        else:
            return "上传失败"
    except Exception as e:
        return f"上传失败: {str(e)}"

if __name__ == '__main__':
    app.run(debug=True, host='localhost', port=80)

服务端以管理员身份运行main.py文件,此时会启用一个Web服务器用于接收客户端的上传请求,如下图所示;

接着是客户端的实现,首先介绍如下几个关键API函数;

HttpSendRequestEx

用于发送带有附加选项的 HTTP 请求。相对于 HttpSendRequest,它提供了更多的灵活性,允许在请求中包含额外的信息,例如头部和数据。

以下是 HttpSendRequestEx 的原型:

BOOL HttpSendRequestEx(
  HINTERNET               hRequest,
  LPINTERNET_BUFFERS      lpBuffersIn,
  LPINTERNET_BUFFERS      lpBuffersOut,
  DWORD                   dwFlags,
  DWORD_PTR               dwContext
);

参数说明:

  • hRequest:由 HttpOpenRequest 返回的句柄,表示 HTTP 请求。
  • lpBuffersIn:指向 INTERNET_BUFFERS 结构的指针,其中包含要作为请求的一部分发送的数据。
  • lpBuffersOut:指向 INTERNET_BUFFERS 结构的指针,用于接收响应中接收到的数据。
  • dwFlags:附加标志,控制函数的行为。这可以包括选项,如 INTERNET_FLAG_RELOADINTERNET_FLAG_SECURE 等。
  • dwContext:传递给回调函数的用户定义的上下文值。

INTERNET_BUFFERS 是一个结构,允许您在 HTTP 请求和响应中指定用于发送和接收数据的缓冲区。

使用 HttpSendRequestEx 需要谨慎处理内存,并根据您的需求设置 INTERNET_BUFFERS 结构的具体方式。

InternetWriteFile

用于将数据写入到由 InternetOpenUrlInternetOpenHttpOpenRequestFtpOpenFile 等函数打开的 URL、连接或文件。以下是该函数的原型:

BOOL InternetWriteFile(
  HINTERNET hFile,
  LPCVOID   lpBuffer,
  DWORD     dwNumberOfBytesToWrite,
  LPDWORD   lpdwNumberOfBytesWritten
);

参数说明:

  • hFile: 调用 InternetOpenUrlInternetOpenHttpOpenRequestFtpOpenFile 等函数返回的句柄,表示要写入的文件、URL 或连接。
  • lpBuffer: 指向包含要写入的数据的缓冲区的指针。
  • dwNumberOfBytesToWrite: 要写入的字节数。
  • lpdwNumberOfBytesWritten: 指向一个变量,表示实际写入的字节数。在调用函数前,应该将该变量设置为缓冲区的大小。在调用函数后,该变量将包含实际写入的字节数。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

InternetWriteFile 主要用于将数据写入网络资源,如通过 HTTP 或 FTP 协议上传文件。在调用此函数之前,通常需要先调用其他相关的函数,如 InternetOpenUrlInternetOpenHttpOpenRequest 等。同样,使用完资源后,需要使用 InternetCloseHandle 函数关闭相应的句柄,以释放资源。

HttpEndRequest

它通常与 HttpSendRequestHttpSendRequestEx 配合使用,用于完成 HTTP 请求的发送,并准备接收服务器的响应。

以下是 HttpEndRequest 函数的原型:

BOOL HttpEndRequest(
  HINTERNET hRequest,
  LPINTERNET_BUFFERS lpBuffersOut,
  DWORD dwFlags,
  DWORD_PTR dwContext
);

参数说明:

  • hRequest: 调用 HttpOpenRequestHttpOpenRequestExHttpSendRequestHttpSendRequestEx 等函数返回的 HTTP 请求句柄。
  • lpBuffersOut: 指向一个 INTERNET_BUFFERS 结构的指针,该结构用于传递关于响应数据的信息。可以为 NULL
  • dwFlags: 一些标志,用于指定结束请求的选项。通常为 0。
  • dwContext: 用户定义的应用程序上下文,将在回调函数中使用。

返回值:

如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 GetLastError 函数获取详细的错误信息。

HttpEndRequest 的主要作用是完成 HTTP 请求的发送,并在请求完成后准备接收服务器的响应。在调用此函数之后,通常会使用 InternetReadFile 函数等来读取服务器的响应数据。

上传文件的完整代码是这样的,如下所示;

#include <iostream>
#include <Windows.h>
#include <WinInet.h>

#pragma comment(lib, "WinInet.lib")

using namespace std;

// 切割路径
BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, 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;
	}
	return bRet;
}

// 从响应信息头信息中获取数据内容长度大小
BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)
{
	int i = 0;
	char szContentLength[MAX_PATH] = { 0 };
	DWORD dwContentLength = 0;
	char szSubStr[] = "Content-Length: ";
	RtlZeroMemory(szContentLength, MAX_PATH);

	// 在传入字符串中查找子串
	char* p = strstr(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 HttpUpload(char* pszUploadUrl, BYTE* pUploadData, DWORD dwUploadDataSize)
{
	// 初始化变量中的内容
	char szScheme[MAX_PATH] = { 0 };
	char szHostName[MAX_PATH] = { 0 };
	char szUserName[MAX_PATH] = { 0 };
	char szPassword[MAX_PATH] = { 0 };
	char szUrlPath[MAX_PATH] = { 0 };
	char 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);
	
	// 分解URL地址
	if (FALSE == UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))
	{
		return FALSE;
	}

	// 数据上传
	HINTERNET hInternet = NULL;
	HINTERNET hConnect = NULL;
	HINTERNET hRequest = NULL;
	DWORD dwOpenRequestFlags = 0;
	BOOL bRet = FALSE;
	DWORD dwRet = 0;
	unsigned char* pResponseHeaderIInfo = NULL;
	DWORD dwResponseHeaderIInfoSize = 2048;
	BYTE* pBuf = NULL;
	DWORD dwBufSize = 64 * 1024;
	BYTE* pResponseBodyData = NULL;
	DWORD dwResponseBodyDataSize = 0;
	DWORD dwOffset = 0;
	DWORD dwPostDataSize = dwUploadDataSize;
	INTERNET_BUFFERS internetBuffers = { 0 };

	do
	{
		// 建立会话
		hInternet = InternetOpen("WinInetPost/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
		if (NULL == hInternet)
		{
			break;
		}

		// 建立连接
		hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);
		if (NULL == hConnect)
		{
			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, "POST", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);
		if (NULL == hRequest)
		{
			break;
		}

		// 告诉服务器传输数据的总大小
		RtlZeroMemory(&internetBuffers, sizeof(internetBuffers));
		internetBuffers.dwStructSize = sizeof(internetBuffers);
		internetBuffers.dwBufferTotal = dwPostDataSize;

		bRet = HttpSendRequestEx(hRequest, &internetBuffers, NULL, 0, 0);
		if (FALSE == bRet)
		{
			break;
		}

		// 发送数据
		bRet = InternetWriteFile(hRequest, pUploadData, dwUploadDataSize, &dwRet);
		if (FALSE == bRet)
		{
			break;
		}

		// 发送完毕
		bRet = HttpEndRequest(hRequest, NULL, 0, 0);
		if (FALSE == bRet)
		{
			break;
		}

		// 接收响应报文
		pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];
		if (NULL == pResponseHeaderIInfo)
		{
			break;
		}
		RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);
		bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);
		if (FALSE == bRet)
		{
			break;
		}

		// 获取数据长度
		bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwResponseBodyDataSize);
		if (FALSE == bRet)
		{
			break;
		}

		// 输出响应头
		std::cout << pResponseHeaderIInfo << std::endl;

		// 接收报文主体内容(Get Response Body)
		pBuf = new BYTE[dwBufSize];
		if (NULL == pBuf)
		{
			break;
		}
		pResponseBodyData = new BYTE[dwResponseBodyDataSize];
		if (NULL == pResponseBodyData)
		{
			break;
		}
		RtlZeroMemory(pResponseBodyData, dwResponseBodyDataSize);

		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 (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;
}

上传代码通过指定szHttpUploadUrld://lyshark.exe文件提交到远程主机,代码如下所示;

int main(int argc, char* argv[])
{
	// 设置上传接口地址
	char szHttpUploadUrl[] = "http://127.0.0.1/upload?file=lyshark.exe";

	// 被上传文件绝对路径
	char szHttpUploadFileName[] = "d://lyshark.exe";
	BYTE* pHttpUploadData = NULL;
	DWORD dwHttpUploadDataSize = 0;
	DWORD dwRet = 0;
	
	// 打开文件
	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);
	
	// 读取文件数据
	pHttpUploadData = new BYTE[dwHttpUploadDataSize];
	ReadFile(hFile, pHttpUploadData, dwHttpUploadDataSize, &dwRet, NULL);
	dwHttpUploadDataSize = dwRet;
	
	// 上传数据
	if (FALSE == HttpUpload(szHttpUploadUrl, pHttpUploadData, dwHttpUploadDataSize))
	{
		return 0;
	}

	// 释放内存
	delete[]pHttpUploadData;
	pHttpUploadData = NULL;
	CloseHandle(hFile);

	system("pause");
	return 0;
}

运行后提交文件,则可以看到输出信息,如下图所示;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/208533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DynamicDataSource

DynamicDataSource 多数据源&#xff0c;读写分离&#xff0c;主从数据库

7、单片机与W25Q128(FLASH)的通讯(SPI)实验(STM32F407)

SPI接口简介 SPI 是英语Serial Peripheral interface的缩写&#xff0c;顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。 SPI&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且在芯片的管脚上只占用四根…

python使用记录

1、VSCode添加多个python解释器 只需要将对应的python.exe的目录&#xff0c;添加到系统环境变量中即可&#xff0c;VSCode会自动识别及添加 2、pip 使用 pip常用命令和一些坑 查看已安装库的版本号 pip show 库名称 通过git 仓库安装第三方库 pip install git仓库地址

基于Java SSM框架实现汽车在线销售系统项目【项目源码+论文说明】

基于java的SSM框架实现汽车在线销售系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

java基于Spring Boot+vue的民宿客房租赁预订系统的设计与实现含源码数据库

民宿租赁系统在对开发工具的选择上也很慎重&#xff0c;为了便于开发实现&#xff0c;选择的开发工具为IDEA&#xff0c;选择的数据库工具为Mysql。以此搭建开发环境实现民宿租赁系统的功能。其中管理员管理用户&#xff0c;新闻公告。 民宿租赁系统是一款运用软件开发技术设计…

cpu版本的torch可以用清华镜像源安装

一、来到pytroch官网找到如下代码 官方提供的默认的安装cpu版本的torch的命令 pip3 install torch torchvision torchaudio二、使用清华镜像安装 pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple

前端大文件上传webuploader(react + umi)

使用WebUploader还可以批量上传文件、支持缩略图等等众多参数选项可设置&#xff0c;以及多个事件方法可调用&#xff0c;你可以随心所欲的定制你要的上传组件。 分片上传 1.什么是分片上传 分片上传&#xff0c;就是将所要上传的文件&#xff0c;按照一定的大小&#xff0c;将…

股票要怎么买入卖出?

股票账户终于开好了&#xff01;恭喜你马上就可以开启刺激的炒股之旅了&#xff01;不过第一次买股票的你是不是还不知道怎么个买法呢&#xff1f;别担心~贴心的汇小鲸带着教程来了&#xff0c;咱们一起看看吧&#xff01; 首先一点&#xff0c;大家得知道&#xff1a;开好户还…

Leetcode 136. 只出现一次的数字

class Solution {//任何数与0异或结果都是原来的数//任何数和自身异或结果都是0//异或满足交换律和结合律//a ^ b ^ a (a ^ a) ^ b 0 ^ b bpublic int singleNumber(int[] nums) {int res nums[0];for(int i 1; i < nums.length; i){res ^ nums[i];}return res;} }

2023第十二届“认证杯”数学中国数学建模国际赛赛题A完整解析

A题完整题解 写在前面假设数据预处理 问题一1 基于自适应ARIMA-BP神经网络模型的影响因素预测1.1 ARIMA模型的建立1.2 BP神经网络模型的建立1.3 基于GABP神经网络的预测模型构建1.4 自适应混合ARIMA-BP神经网络模型的建立1.5 模型求解 代码Q1_1.mQ1_2.m 完整代码与论文获取 写在…

JavaScript短路求值

之前我们学习过两个运算符&#xff0c;and和OR&#xff0c;但是我们之前还没有发挥出他们全部的潜力&#xff1b;它们也可以用于短路求值&#xff1b; OR || 可以使用任何值&#xff0c;也可以返回任何值&#xff08;不仅仅是布尔型&#xff09;&#xff0c;我们成为短路求值…

数据结构—二叉树

文章目录 10.二叉树(1).二叉树的基本概念(2).遍历#1.前序遍历#2.中序遍历#3.后序遍历#4.非递归中序遍历 (3).中序前/后序建树#1.中序前序遍历建树#2.中序后序遍历建树 (4).递归和二叉树基本操作#1.求树高#2.求结点数#3.求叶子结点数#4.复制树#5.判断两棵树是否相等 (5).特殊二叉…

xss漏洞后端进行html消毒

import org.jsoup.Jsoup;public static String sanitizeHtml(String input) {// 使用 Jsoup 消毒 HTMLreturn Jsoup.clean(input, Safelist.relaxed());}public static void main(String[] args) {String userInput "<p><script>alert(1)</script>Safe…

Linux:vim的简单使用

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、vim的基本概念二、vim的基本操作三、vim正常模式命令集四、vim底行模式命令集五、.xxx.swp的解决总结 前言 本文是对Linux中vim使用的总结 一、vim的基本概念 …

02-使用Git命令操作远程仓库,如克隆或添加远程仓库,拉取或推送内容

操作远程仓库 创建远程仓库 第一步: 访问GitHub官网,登录自己的账号创建一个远程仓库 第二步: 设置远程仓库的信息(一般远程库的名字和本地库的名字一样),仓库创建完成后可以看到仓库地址(每个仓库都会对应一个网络地址) 第三步: 查看本地仓库对应的Https/SSH连接 远程仓库命…

apk和小程序渗透测试

apk和小程序渗透测试 文章目录 apk和小程序渗透测试小程序渗透测试apk和小程序的抓包安装证书apk渗透 小程序渗透测试 小程序的默认路径在 C:\Program Files (x86)\Tencent\WeChat\WeChatApp 使用UnpackMiniApp、wxappUnpacker工具完成逆向 先打开UnpackMiniApp.exe工具 选…

Kotlin学习——kt里的作用域函数scope function,let,run,with,apply,also

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

超全能!PDF转换+套用模板+在线制作电子画册的网站

上班族的朋友们&#xff0c;制作电子画册很苦恼吧&#xff01;没有专业设计水准的&#xff0c;或是想偷偷小懒的看看这里&#xff01;小编说的这些对你的工作非常有效&#xff01; 想要轻轻松松就能制作能与专业设计师们媲美的电子画册&#xff0c;当然是需要一款超全能的工具&…

ESP32CAM视频查看实践

利用ESP32cam的摄像头进行视频查看。视频画质确实模糊&#xff0c;不过不妨碍其高性价比。 1、选择开发板及串口 2、打开例程 3、修改热点和摄像模式 4、复制链接查看 手机浏览器打开

LeetCode Hot100 169.多数元素

题目&#xff1a; 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 方法一&#xff1a;哈希表 ​ class Solution {public int…