You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:18:10