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

使用WinHTTP的C++向Flask发送multipart/form-data失败排查

解决WinHTTP发送multipart/form-data时Flask无法识别request.files的问题

问题现象

用C++ WinHTTP向Flask服务器发送文件上传请求(multipart/form-data格式):

  • Flask端通过request.files获取文件时返回ImmutableMultiDict([]),完全无法识别文件字段
  • 但通过request.get_data()或request.data能拿到完整的请求原始数据
  • 期望request.files能正确解析出文件,输出类似:ImmutableMultiDict([('file', <FileStorage: 'img.png' ('image/png')>)])

错误原因

  1. 编码不匹配:HTTP协议要求multipart/form-data的边界、头部必须使用单字节ASCII编码,但原代码用std::wstring构造请求头部和表单数据,发送时按UTF-16宽字节传输,导致Flask无法正确解析边界分隔符。
  2. 文件路径错误:原代码中filePath = L"C:img.png"缺少路径分隔符,应为L"C:\\img.png",会导致文件名解析异常。
  3. Content-Length计算与实际发送字节数不匹配:宽字符串的字节数计算逻辑正确,但因编码问题实际发送的内容和声明的长度不符,导致Flask解析中断。

修复方案

1. 统一使用单字节字符串处理HTTP请求内容

将所有HTTP头部、表单边界、表单描述部分从std::wstring转换为std::string,确保以ASCII编码传输。

2. 修正文件路径格式

补全文件路径的分隔符,避免文件名错误。

3. 确保multipart格式符合规范

严格遵循RFC 7578规范:每个表单部分以--boundary开头,后跟头部字段,空行分隔头部和文件内容,最后以--boundary--结束。

修正后的完整C++代码

#include <windows.h>
#include <winhttp.h>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>

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

// 读取文件内容到vector<char>
std::vector<char> ReadFileContent(const std::wstring& filePath) {
    std::vector<char> content;
    HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        return content;
    }

    DWORD fileSize = GetFileSize(hFile, NULL);
    content.resize(fileSize);
    DWORD bytesRead = 0;
    ReadFile(hFile, content.data(), fileSize, &bytesRead, NULL);
    CloseHandle(hFile);
    return content;
}

// 宽字符串转多字节字符串
std::string WideToMultiByte(const std::wstring& wstr) {
    int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
    std::string str(bufferSize, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, str.data(), bufferSize, NULL, NULL);
    // 去掉末尾的'\0'
    str.pop_back();
    return str;
}

int main() {
    std::wstring filePath = L"C:\\img.png";
    std::vector<char> fileContent = ReadFileContent(filePath);
    if (fileContent.empty()) {
        std::wcerr << L"Failed to read file!" << std::endl;
        return 1;
    }

    HINTERNET hSession = WinHttpOpen(L"WinHTTP Example/1.0",
        WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) {
        std::wcerr << L"WinHttpOpen failed!" << std::endl;
        return 1;
    }

    HINTERNET hConnect = WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, 0);
    if (!hConnect) {
        std::wcerr << L"WinHttpConnect failed!" << std::endl;
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 生成单字节的boundary
    std::string boundary = "--------------------------" + std::to_string(GetTickCount64());

    // 构造HTTP头部
    std::string headers = "Content-Type: multipart/form-data; boundary=" + boundary;

    // 构造表单头部:提取文件名,替换宽路径为文件名
    size_t lastSlash = filePath.find_last_of(L"\\/");
    std::wstring fileName = (lastSlash != std::wstring::npos) ? filePath.substr(lastSlash + 1) : filePath;
    std::string formData = "--" + boundary + "\r\n"
        "Content-Disposition: form-data; name=\"file\"; filename=\"" + WideToMultiByte(fileName) + "\"\r\n"
        "Content-Type: image/png\r\n\r\n";
    std::string finalBoundary = "\r\n--" + boundary + "--\r\n";

    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST",
        L"/upload",
        NULL, WINHTTP_NO_REFERER,
        WINHTTP_DEFAULT_ACCEPT_TYPES,
        0);
    if (!hRequest) {
        std::wcerr << L"WinHttpOpenRequest failed!" << std::endl;
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    DWORD dwOption = WINHTTP_OPTION_REDIRECT_POLICY_NEVER;
    WinHttpSetOption(hRequest, WINHTTP_OPTION_REDIRECT_POLICY, &dwOption, sizeof(dwOption));

    // 计算总内容长度:表单头部字节数 + 文件内容字节数 + 结束边界字节数
    DWORD totalLength = formData.size() + fileContent.size() + finalBoundary.size();

    BOOL bResults = WinHttpSendRequest(hRequest, headers.c_str(),
        headers.size(), 0, 0, totalLength, 0);
    if (!bResults) {
        std::wcerr << L"WinHttpSendRequest failed: " << GetLastError() << std::endl;
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 发送表单头部
    bResults = WinHttpWriteData(hRequest, formData.data(), formData.size(), NULL);
    if (!bResults) {
        std::wcerr << L"WinHttpWriteData (formData) failed: " << GetLastError() << std::endl;
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 发送文件内容
    bResults = WinHttpWriteData(hRequest, fileContent.data(), fileContent.size(), NULL);
    if (!bResults) {
        std::wcerr << L"WinHttpWriteData (file) failed: " << GetLastError() << std::endl;
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 发送结束边界
    bResults = WinHttpWriteData(hRequest, finalBoundary.data(), finalBoundary.size(), NULL);
    if (!bResults) {
        std::wcerr << L"WinHttpWriteData (finalBoundary) failed: " << GetLastError() << std::endl;
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    bResults = WinHttpReceiveResponse(hRequest, NULL);
    if (!bResults) {
        std::wcerr << L"WinHttpReceiveResponse failed: " << GetLastError() << std::endl;
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // 读取响应(可选)
    char buffer[1024];
    DWORD bytesRead = 0;
    while (WinHttpReadData(hRequest, buffer, sizeof(buffer)-1, &bytesRead) && bytesRead > 0) {
        buffer[bytesRead] = '\0';
        std::cout << "Response: " << buffer << std::endl;
    }

    // 清理资源
    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);

    return 0;
}

Flask测试代码

from flask import Flask, request

app = Flask(__name__)

@app.route('/upload', methods=['POST'])
def upload():
    print("request.files:", request.files)
    print("request.content_type:", request.content_type)
    return "Upload Success"

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

验证结果

运行修正后的C++代码,Flask终端会输出:

request.files: ImmutableMultiDict([('file', <FileStorage: 'img.png' ('image/png')>)])
request.content_type: multipart/form-data; boundary=--------------------------123456789012345678901234

内容的提问来源于stack exchange,提问作者이진영

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:22:02