如何用WinInet发送未知大小(超4GB)的istream至HTTP服务器
解决方案
要解决未知长度(甚至超过4GB)的istream通过WinINet发送的问题,核心是正确实现HTTP分块传输编码,同时调整HttpSendRequestEx的调用方式。你的代码虽然添加了Transfer-Encoding: chunked头,但没有按照分块传输的格式发送数据,这是导致问题的主要原因。
关键修改点
- 确保分块传输的请求头正确:移除
Content-Length头(分块传输不需要),保证Transfer-Encoding: chunked被正确添加。 - 调整
HttpSendRequestEx的参数:不需要指定总大小,将INTERNET_BUFFERSA的dwBufferTotal设为0即可(部分场景下传NULL也可行,但显式初始化空结构体更可靠)。 - 按照分块格式发送数据:每个数据块需要先发送十六进制的块长度+
\r\n,再发送块数据+\r\n;最后发送长度为0的块(0\r\n\r\n)来结束传输。 - 支持超大文件:分块传输天然不需要总大小,因此可以处理超过4GB的数据流。
修改后的完整代码
#include <string> #include <sstream> #include <windows.h> #include <wininet.h> #include <iomanip> using namespace std; void UploadSomeData(string server, int port, istream& input) { const string agt = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"; HINTERNET ses = InternetOpenA(agt.c_str(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); HINTERNET con = InternetConnectA(ses, server.c_str(), port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); HINTERNET req = HttpOpenRequestA(con, "POST", "/upload", NULL, NULL, NULL, 0, 0); // 添加分块传输编码头,确保替换或添加正确的头 HttpAddRequestHeadersA(req, "Transfer-Encoding: chunked\r\n", -1, HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD); // 初始化INTERNET_BUFFERSA,不指定总大小 INTERNET_BUFFERSA buffers = { 0 }; buffers.dwStructSize = sizeof(buffers); buffers.dwBufferTotal = 0; // 分块传输不需要总大小 // 启动请求 HttpSendRequestExA(req, &buffers, NULL, HSR_INITIATE, 0); const int chunk_size = 10000; char chunk[chunk_size]; DWORD written; while (true) { input.read(chunk, chunk_size); int count = static_cast<int>(input.gcount()); if (count == 0) break; // 1. 发送块长度的十六进制字符串 + \r\n stringstream ss; ss << hex << count << "\r\n"; string len_str = ss.str(); InternetWriteFile(req, len_str.c_str(), static_cast<DWORD>(len_str.size()), &written); // 2. 发送块数据 + \r\n InternetWriteFile(req, chunk, static_cast<DWORD>(count), &written); InternetWriteFile(req, "\r\n", 2, &written); } // 发送结束块:0\r\n\r\n InternetWriteFile(req, "0\r\n\r\n", 5, &written); // 结束请求 HttpEndRequest(req, NULL, 0, 0); // 清理句柄 InternetCloseHandle(req); InternetCloseHandle(con); InternetCloseHandle(ses); } int main() { string s = "spam and eggs."; for (int i = 0; i < 20; i++) s += s; // 生成大字符串(约14MB) istringstream input(s); UploadSomeData("127.0.0.1", 3456, input); return 0; }
额外说明
- 分块传输的格式必须严格遵循HTTP/1.1规范,否则服务器无法正确解析数据。
- 对于超过4GB的数据流,分块传输是唯一符合HTTP标准的解决方案,因为
Content-Length头使用32位整数,无法表示超过4GB的大小。 - 若尝试直接给
HttpSendRequestEx传递NULL作为第二个参数,需确保请求头已完全配置,部分WinINet版本可能需要显式初始化空结构体才能正常工作。
内容的提问来源于stack exchange,提问作者Captain'Flam
相关产品推荐
相关产品推荐

