WinHTTP实现文件下载 C++程序

简单粗暴的书写

使用WinHTTP实现文件下载,支持HTTPS。
支持保存文件路径,支持下载文件命名。
添加UrlEncode和UrlDecode(非标准规范,仅便于解析后获取文件名称)。
无暂停或继续下载
Usage: ProjectName.exe [Url] [drive:\path\filename]

#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <iostream>

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

//https://blog.csdn.net/c914620529/article/details/73503708
//ANSI对应char,UTF-8对应char类型,Unicode(UTF-16)对应wchar_t
//ANSI字符串的英文使用一个字节,中文使用两个字节
//Unicode字符串的英文与中文都使用两个字节
//UTF8字符串的英文使用一个字节,中文使用三个字节

//ANSI-->Unicode
std::wstring StringToWString(const std::string& str)
{
	int num = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
	wchar_t* wide = new wchar_t[num];
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wide, num);
	std::wstring wstr = wide;
	delete[] wide;
	return wstr;
}

//Unicode-->ANSI
std::string WStringToString(const std::wstring& wszString)
{
	int num = WideCharToMultiByte(CP_ACP, 0, wszString.c_str(), -1, NULL, 0, NULL, NULL);
	char* wide = new char[num];
	WideCharToMultiByte(CP_ACP, 0, wszString.c_str(), -1, wide, num, NULL, NULL);
	std::string str = wide;
	delete[] wide;
	return str;
}

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

//via @Dewei
//修改UrlEncode部分
std::string urlencode(std::string& str_source)
{
	char const* in_str = str_source.c_str();
	int in_str_len = (int)strlen(in_str);
	int out_str_len = 0;
	std::string out_str;
	register unsigned char c;
	unsigned char* to, * start;
	unsigned char const* from, * end;
	unsigned char hexchars[] = "0123456789ABCDEF";

	from = (unsigned char*)in_str;
	end = (unsigned char*)in_str + strlen(in_str);
	start = to = (unsigned char*)malloc(3 * strlen(in_str) + 1);

	while (from < end) {
		c = *from++;
		if ((c < '0' && c != '-' && c != '.' && c != '/' && c != '%') ||
			(c < 'A' && c > '9' && c != ':') ||
			(c > 'Z' && c < 'a' && c != '_') ||
			(c > 'z')) {
			to[0] = '%';
			to[1] = hexchars[c >> 4];
			to[2] = hexchars[c & 15];
			to += 3;
		}
		else {
			*to++ = c;
		}
	}
	*to = 0;

	out_str_len = (int)(to - start);
	out_str = (char*)start;
	free(start);
	return out_str;
}

//via @Dewei
static int php_htoi(char* s)
{
	int value;
	int c;

	c = ((unsigned char*)s)[0];
	if (isupper(c))
		c = tolower(c);
	value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;

	c = ((unsigned char*)s)[1];
	if (isupper(c))
		c = tolower(c);
	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;

	return (value);
}
//via @Dewei
//修改UrlDecode部分
std::string urldecode(std::string& str_source)
{
	char const* in_str = str_source.c_str();
	int in_str_len = (int)strlen(in_str);
	int out_str_len = 0;
	std::string out_str;
	char* str;

	str = _strdup(in_str);
	char* dest = str;
	char* data = str;

	while (in_str_len--) {
		if (*data == '%' && in_str_len >= 2 && isxdigit((int)*(data + 1))
			&& isxdigit((int)*(data + 2))) {
			*dest = (char)php_htoi(data + 1);
			data += 2;
			in_str_len -= 2;
		}
		else {
			*dest = *data;
		}
		data++;
		dest++;
	}
	*dest = '\0';
	out_str_len = (int)(dest - str);
	out_str = str;
	free(str);
	return out_str;
}

std::string GetFileName(std::string &str_path)
{
	unsigned long long pos = str_path.find_last_of("/");
	std::string filename=str_path.substr(pos + 1);
	return filename;
}

int winhttpDownload(std::string Url, std::string FileName)
{
	std::wstring wUrl = StringToWString(Url);
	LPCWSTR pwszUrlwstr = wUrl.c_str();

	URL_COMPONENTS urlComp = { 0 };
	DWORD dwUrlLen = 0;
	// 初始化 URL_COMPONENTS 结构体.
	ZeroMemory(&urlComp, sizeof(urlComp));
	urlComp.dwStructSize = sizeof(urlComp);
	//分配空间存储,否则解析获取的信息格式不正确
	wchar_t* pScheme = new wchar_t[MAX_PATH]();
	wchar_t* pHostName = new wchar_t[MAX_PATH]();
	wchar_t* pUserName = new wchar_t[MAX_PATH]();
	wchar_t* pPassword = new wchar_t[MAX_PATH]();
	wchar_t* pUrlPath = new wchar_t[MAX_PATH]();
	wchar_t* pExtraInfo = new wchar_t[MAX_PATH]();

	urlComp.lpszScheme = pScheme;
	urlComp.lpszHostName = pHostName;
	urlComp.lpszUserName = pUserName;
	urlComp.lpszPassword = pPassword;
	urlComp.lpszUrlPath = pUrlPath;
	urlComp.lpszExtraInfo = pExtraInfo;

	// 设置必要的组件长度为非零,这样它们就可以被解析.(结构体连等赋值)
	urlComp.dwSchemeLength =
		urlComp.dwHostNameLength =
		urlComp.dwUserNameLength =
		urlComp.dwPasswordLength =
		urlComp.dwUrlPathLength =
		urlComp.dwExtraInfoLength = (DWORD)-1;

	//解析
	//if (!WinHttpCrackUrl(pwszUrlwstr, (DWORD)wcslen(pwszUrlwstr), 0, &urlComp))
	if (!WinHttpCrackUrl(pwszUrlwstr, 0, 0, &urlComp))
	{
		printf("Error %u in WinHttpCrackUrl.\n", GetLastError());
	}
	else
	{
		std::wstring wUrlPath = pUrlPath;
		std::string u8strUrl = uWStringTou8String(wUrlPath);
		std::string strUrlen = urlencode(u8strUrl); //UrlEncode后均为英文字符,ANSI与UTF-8无差别
		std::wstring wstrUrlen = StringToWString(strUrlen);
		//同样可以使用u8StringTouWString() 转换为Unicode
		urlComp.lpszUrlPath = (LPWSTR)wstrUrlen.c_str();
		std::string strUrlde = urldecode(strUrlen);
		std::wstring u8UrlPath = u8StringTouWString(strUrlde);
		std::string strUrlPath = WStringToString(u8UrlPath);

		if (FileName.empty() == 1)
		{
			FileName = GetFileName(strUrlPath);
		}
		
		LPCSTR lpFileNamestr = FileName.c_str();

		DWORD dwSize = 0;
		DWORD dwSumSize = 0;
		DWORD dwDownloaded = 0;
		DWORD dwBuffer = 0,
			dwBufferLength = sizeof(DWORD),
			dwIndex = 0;

		LPSTR pszOutBuffer;
		BOOL  bResults = FALSE;
		HINTERNET  hSession = NULL,
			hConnect = NULL,
			hRequest = NULL;

		HANDLE hFile;

		hFile = CreateFileA(lpFileNamestr,  // creates a new file
			FILE_APPEND_DATA,         // open for writing
			FILE_SHARE_READ,          // allow multiple readers
			NULL,                     // no security
			CREATE_ALWAYS,            // creates a new file, always.
			FILE_ATTRIBUTE_NORMAL,    // normal file
			NULL);                    // no attr. template

		if (hFile == INVALID_HANDLE_VALUE)
		{
			printf("Could not creates %s.\n", lpFileNamestr);
			return 2;
		}

		// Use WinHttpOpen to obtain a session handle.
		hSession = WinHttpOpen(L"WinHTTP_Download Example/1.0",
			WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
			WINHTTP_NO_PROXY_NAME,
			WINHTTP_NO_PROXY_BYPASS, 0);

		// Specify an HTTP server.
		//INTERNET_PORT nPort = (pGetRequest->fUseSSL) ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;

		if (hSession)
			hConnect = WinHttpConnect(hSession, urlComp.lpszHostName,
				//hConnect = WinHttpConnect(hSession, L"avatar.csdn.net",
				INTERNET_DEFAULT_HTTPS_PORT, 0);

		// Create an HTTP request handle.
		if (hConnect)
			hRequest = WinHttpOpenRequest(hConnect, L"GET", urlComp.lpszUrlPath,
				L"HTTP/1.1", WINHTTP_NO_REFERER,
				WINHTTP_DEFAULT_ACCEPT_TYPES,
				WINHTTP_FLAG_SECURE);

		// Send a request.
		if (hRequest)
			bResults = WinHttpSendRequest(hRequest,
				WINHTTP_NO_ADDITIONAL_HEADERS,
				0, WINHTTP_NO_REQUEST_DATA, 0,
				0, 0);

		// End the request.
		if (bResults)
			bResults = WinHttpReceiveResponse(hRequest, NULL);

		WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwBuffer, &dwBufferLength, &dwIndex);

		// Continue to verify data until there is nothing left.
		if (bResults)
			do
			{
				// Verify available data.
				dwSize = 0;
				if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
					printf("Error %u in WinHttpQueryDataAvailable.\n",
						GetLastError());

				// Allocate space for the buffer.
				pszOutBuffer = new char[dwSize + (size_t)1];
				if (!pszOutBuffer)
				{
					printf("Out of memory\n");
					dwSize = 0;
				}
				else
				{
					dwSumSize += dwSize;
					printf("Download: %0.2f%%\t%d\t%d\r", dwSumSize * 100.0 / dwBuffer, dwSumSize, dwBuffer); //计算已下载的百分比
					// Read the Data.
					ZeroMemory(pszOutBuffer, dwSize + (size_t)1);

					if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
						dwSize, &dwDownloaded)) {
						printf("Error %u in WinHttpReadData.\n", GetLastError());
					}
					else {
						WriteFile(hFile, pszOutBuffer, dwDownloaded, &dwDownloaded, NULL);
					}
					// Free the memory allocated to the buffer.
					delete[] pszOutBuffer;
				}

			} while (dwSize > 0);

			// Close files.
			CloseHandle(hFile);

			// Report any errors.
			if (!bResults)
				printf("Error %d has occurred.\n", GetLastError());

			// Close open handles.
			if (hRequest) WinHttpCloseHandle(hRequest);
			if (hConnect) WinHttpCloseHandle(hConnect);
			if (hSession) WinHttpCloseHandle(hSession);

			delete[]pScheme;
			delete[]pHostName;
			delete[]pUserName;
			delete[]pPassword;
			delete[]pUrlPath;
			delete[]pExtraInfo;
	}
	return 0;
}

int main(int argc, CHAR* argv[])
//"https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpconnect"
{
	if (argc < 2)
	{
		printf("Err: Must have at least one parameter. argc:%d(argc>=2)",argc);
	}
	else if (argc == 2)
	{
		winhttpDownload(argv[1], "");
	}
	else
	{
		winhttpDownload(argv[1], argv[2]);
	}
	return 0;
}

逗是简单粗暴的用C++写的使用WinHTTP实现文件下载,不再被PowerShell安全策略支配(执行*.ps1脚本却忘记修改PowerShell的安全策略允许运行脚本),下载功能和 Invoke-WebRequest -Uri *** -OutFile *** 一样一样的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WinHTTP提供以下功能: WinHttpAddRequestHeaders 向HTTP请求句柄添加一个或多个HTTP请求标头。 WinHttpCheckPlatform 确定WinHTTP是否支持当前平台。 WinHttpCloseHandle 关闭单个 HINTERNET句柄。 WinHttpConnect 指定HTTP请求的初始目标服务器。 WinHttpCrackUrl 将URL分为其组成部分,例如主机名和路径。 WinHttpCreateProxyResolver 创建WinHttpGetProxyForUrlEx使用的句柄。 WinHttpCreateUrl 从组件部分创建URL,例如主机名和路径。 WinHttpDetectAutoProxyConfigUrl 查找代理自动配置(PAC)文件的URL。此功能报告PAC文件的URL,但不下载文件WinHttpFreeProxyResult 释放从以前的调用WinHttpGetProxyResult检索的数据。 WinHttpGetDefaultProxyConfiguration 从注册表中检索默认的WinHTTP代理配置。 WinHTTPGetIEProxyConfigForCurrentUser 获取当前用户的Internet Explorer(IE)代理配置。 WinHttpGetProxyForUrl 检索指定URL的代理信息。 WinHttpGetProxyForUrlEx 检索指定URL的代理信息。 WinHttpGetProxyResult 检索到调用的结果WinHttpGetProxyForUrlEx。 WinHttpOpen 初始化应用程序WinHTTP功能的使用。 WinHttpOpenRequest 创建HTTP请求句柄。 WinHttpQueryAuthSchemes 返回服务器支持的授权方案。 WinHttpQueryDataAvailable 返回可立即与读取数据的字节数 WinHttpReadData。 WinHttpQueryHeaders 检索与HTTP请求相关联的头信息。 WinHttpQueryOption 在指定的句柄上查询Internet选项。 WinHttpReadData 从WinHttpOpenRequest函数打开的句柄中读取数据 。 WinHttpReceiveResponse 结束由WinHttpSendRequest启动的HTTP请求 。 WinHttpResetAutoProxy 重置自动代理。 WinHttpSendRequest 将指定的请求发送到HTTP服务器。 WinHttpSetCredentials 将所需的授权凭证传递给服务器。 WinHttpSetDefaultProxyConfiguration 在注册表中设置默认的WinHTTP代理配置。 WinHttpSetOption 设置Internet选项。 WinHttpSetStatusCallback 设置WinHTTP可以在操作过程中进行调用的回调函数。 WinHttpSetTimeouts 设置涉及HTTP事务的各种超时。 WinHttpTimeFromSystemTime 根据HTTP版本1.0规范格式化日期和时间。 WinHttpTimeToSystemTime 获取HTTP时间/日期字符串并将其转换为 SYSTEMTIME结构。 WinHttpWriteData 将请求数据写入HTTP服务器。 WinHttpWebSocketClose 关闭WebSocket连接。 WinHttpWebSocketCompleteUpgrade 完成由WinHttpSendRequest启动的WebSocket握手。 WinHttpWebSocketQueryCloseStatus 获取服务器发送的关闭状态。 WinHttpWebSocketReceive 从WebSocket连接接收数据。 WinHttpWebSocketSend 通过WebSocket连接发送数据。 WinHttpWebSocketShutdown 向WebSocket连接发送关闭框架

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值