【问题标题】:WinInet upload fileWinInet 上传文件
【发布时间】:2017-12-19 00:51:12
【问题描述】:

我编写了一个 C++ 函数来使用 WinInet 库执行上传文件。代码编译执行不返回错误,但我无法在服务器端处理文件。 php 和 python 服务器都不能识别文件。 这是代码:

#define DEFAULT_USERAGENT "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" 
#define MY_HOST "192.168.1.101"
#define ALT_HTTP_PORT 8080
#define METHOD_POST "POST"
#define BUFSIZE 1024

void UploadFile()
{
    char szHeaders[] = "Content-Type: multipart/form-data; boundary=---------------------------974767299852498929531610575";
    char szContent[] = "---------------------------974767299852498929531610575\r\nContent-Disposition: form-data; name=\"file\"; filename=\"main.cpp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
    char szEndData[] = "\r\n---------------------------974767299852498929531610575--\r\n";
    unsigned char c;
    char szBuffer[BUFSIZE];
    memset(szBuffer, 0, BUFSIZE);
    char* szData = NULL;
    DWORD dwBytes;
    HANDLE hIn = CreateFile("main.cpp", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    for(int i = 0; i < BUFSIZE - 1 ; i++){
        ReadFile(hIn, &c, 1, NULL, NULL);
        szBuffer[i] = c;
    }
    szBuffer[BUFSIZE - 1] = '\0';
    CloseHandle(hIn);
    size_t sDataSize = strlen(szBuffer) + strlen(szContent) + strlen(szEndData) + 1;
    szData = new char[sDataSize];
    SecureZeroMemory(szData, sizeof(szData));
    strcat(szData, szContent);
    strcat(szData, szBuffer);
    strcat(szData, szEndData);
    szData[sDataSize] = '\0';

    HINTERNET io = InternetOpen(DEFAULT_USERAGENT, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if(!io){
        std::cerr << "InternetOpen Error" << std::endl;
        return;
    }
    HINTERNET ic = InternetConnect(io, MY_HOST, ALT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if(!ic){
        std::cerr << "InternetConnect Error" << std::endl;
        return;
    }

    HINTERNET hreq = HttpOpenRequest(ic, METHOD_POST, "/upload", NULL,NULL, NULL, 0, 0);
    if(!hreq){
        std::cerr << "HttpOpenRequest Error" << std::endl;
        return;
    }
    HttpSendRequest(hreq, szHeaders, strlen(szHeaders), szData, strlen(szData));
    InternetCloseHandle(io);
    InternetCloseHandle(ic);
    InternetCloseHandle(hreq);
    delete[] szData;
}

【问题讨论】:

    标签: c++ http winapi wininet


    【解决方案1】:

    szContentszEndData 中的 MIME 边界线是错误的。阅读RFC 2046。内容数据中的边界线以两个 - 字符开头,然后是 Content-Type 标头的 boundary 参数中指定的文本(在结束边界线的情况下,然后是另外两个 @987654327 @ 字符)。

    您在内容数据中使用的边界线缺少这两个前导 - 字符。 boundary 标头中有 27 个前导破折号,因此内容中需要 29 个前导破折号,而不是像您那样的 27 个。

    另外,您假设输入文件的大小正好是 1023 字节。但是,更重要的是,您没有对 CreateFile() 调用进行任何错误处理以确保文件实际上是打开的,或者对 ReadFile() 调用进行任何错误处理以确保实际读取数据(提示:当第5个参数为NULL时,第4个参数不能为NULL)。

    您正在尝试将整个文件(嗯,最多 1023 个字节)读入内存,然后发送。至少,您应该使用GetFileSize() 来获取实际的文件大小,然后相应地分配szData 并直接读取它。你根本不需要szBuffer

    您的代码中还有其他小错误。比如误用sizeof(),假设文件数据中不包含任何空字节,内存泄漏等。

    试试类似的方法:

    #define DEFAULT_USERAGENT "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" 
    #define MY_HOST "192.168.1.101"
    #define ALT_HTTP_PORT 8080
    #define METHOD_POST "POST"
    
    #include <vector>
    #include <memory>
    
    struct FileCloser
    {
        typedef HANDLE pointer;
    
        void operator()(HANDLE h)
        {
            if (h != INVALID_HANDLE_VALUE)
                CloseHandle(h);
        }
    };
    
    struct InetCloser
    {
        typedef HINTERNET pointer;
    
        void operator()(HINTERNET h)
        {
            if (h != NULL)
                InternetCloseHandle(h);
        }
    }; 
    
    void UploadFile()
    {
        const char *szHeaders = "Content-Type: multipart/form-data; boundary=----974767299852498929531610575";
        const char *szContent = "------974767299852498929531610575\r\nContent-Disposition: form-data; name=\"file\"; filename=\"main.cpp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
        const char *szEndData = "\r\n------974767299852498929531610575--\r\n";
    
        std::unique_ptr<HANDLE, FileCloser> hIn(CreateFile("main.cpp", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL));
        if (hIn.get() == INVALID_HANDLE_VALUE)
        {
            std::cerr << "CreateFile Error" << std::endl;
            return;
        }
    
        DWORD dwFileSize = GetFileSize(hIn.get(), NULL);
        if (dwFileSize == INVALID_FILE_SIZE)
        {
            std::cerr << "GetFileSize Error" << std::endl;
            return;
        }
    
        size_t sContentSize = strlen(szContent);
        size_t sEndDataSize = strlen(szEndData);
    
        std::vector<char> vBuffer(sContentSize + dwFileSize + sEndDataSize);
        char *szData = &vBuffer[0];
    
        memcpy(szData, szContent, sContentSize);
        szData += sContentSize;
    
        DWORD dw = 0, dwBytes;
        while (dw < dwFileSize)
        {
            if (!ReadFile(hIn.get(), szData, dwFileSize-dw, &dwBytes, NULL))
            {
                std::cerr << "ReadFile Error" << std::endl;
                return;
            }
            szData += dwBytes;
            dw += dwBytes;
        }
    
        hIn.reset();
    
        memcpy(szData, szEndData, sEndDataSize);
    
        std::unique_ptr<HINTERNET, InetCloser> io(InternetOpen(DEFAULT_USERAGENT, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0));
        if (io.get() == NULL)
        {
            std::cerr << "InternetOpen Error" << std::endl;
            return;
        }
    
        std::unique_ptr<HINTERNET, InetCloser> ic(InternetConnect(io.get(), MY_HOST, ALT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0));
        if (ic.get() == NULL)
        {
            std::cerr << "InternetConnect Error" << std::endl;
            return;
        }
    
        std::unique_ptr<HINTERNET, InetCloser> hreq(HttpOpenRequest(ic.get(), METHOD_POST, "/upload", NULL, NULL, NULL, 0, 0));
        if (hreq.get() == NULL)
        {
            std::cerr << "HttpOpenRequest Error" << std::endl;
            return;
        }
    
        if (!HttpSendRequest(hreq.get(), szHeaders, -1, &vBuffer[0], vBuffer.size()))
            std::cerr << "HttpSendRequest Error" << std::endl;
    }
    

    另一种选择是改用HttpSendRequestEx(),这样您就可以在循环中使用InternetWriteFile(),在每次循环迭代时以块的形式读取+发送文件。这样,您不必在发送之前将整个文件读入内存:

    #define DEFAULT_USERAGENT "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" 
    #define MY_HOST "192.168.1.101"
    #define ALT_HTTP_PORT 8080
    #define METHOD_POST "POST"
    #define BUFSIZE 1024
        
    #include <memory>
    #include <algorithm>
    
    struct FileCloser
    {
        typedef HANDLE pointer;
    
        void operator()(HANDLE h)
        {
            if (h != INVALID_HANDLE_VALUE)
                CloseHandle(h);
        }
    };
    
    struct InetCloser
    {
        typedef HINTERNET pointer;
    
        void operator()(HINTERNET h)
        {
            if (h != NULL)
                InternetCloseHandle(h);
        }
    }; 
    
    bool WriteToInternet(HINTERNET hInet, const void *Data, DWORD DataSize)
    {
        const BYTE *pData = (const BYTE *) Data;
        DWORD dwBytes;
    
       while (DataSize > 0)
       {
           if (!InternetWriteFile(hInet, pData, DataSize, &dwBytes))
               return false;
           pData += dwBytes;
           DataSize -= dwBytes;
       }
    
       return true;
    }
    
    void UploadFile()
    {
        const char *szHeaders = "Content-Type: multipart/form-data; boundary=----974767299852498929531610575";
        const char *szContent = "------974767299852498929531610575\r\nContent-Disposition: form-data; name=\"file\"; filename=\"main.cpp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
        const char *szEndData = "\r\n------974767299852498929531610575--\r\n";
    
        std::unique_ptr<HANDLE, FileCloser> hIn(CreateFile("main.cpp", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL));
        if (hIn.get() == INVALID_HANDLE_VALUE)
        {
            std::cerr << "CreateFile Error" << std::endl;
            return;
        }
    
        DWORD dwFileSize = GetFileSize(hIn.get(), NULL);
        if (dwFileSize == INVALID_FILE_SIZE)
        {
            std::cerr << "GetFileSize Error" << std::endl;
            return;
        }
    
        std::unique_ptr<HINTERNET, InetCloser> io(InternetOpen(DEFAULT_USERAGENT, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0));
        if (io.get() == NULL)
        {
            std::cerr << "InternetOpen Error" << std::endl;
            return;
        }
    
        std::unique_ptr<HINTERNET, InetCloser> ic(InternetConnect(io.get(), MY_HOST, ALT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0));
        if (ic.get() == NULL)
        {
            std::cerr << "InternetConnect Error" << std::endl;
            return;
        }
    
        std::unique_ptr<HINTERNET, InetCloser> hreq(HttpOpenRequest(ic.get(), METHOD_POST, "/upload", NULL, NULL, NULL, 0, 0));
        if (hreq.get() == NULL)
        {
            std::cerr << "HttpOpenRequest Error" << std::endl;
            return;
        }
    
        if (!HttpAddRequestHeaders(hreq.get(), szHeaders, -1, HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD))
        {
            std::cerr << "HttpAddRequestHeaders Error" << std::endl;
            return;
        } 
    
        size_t sContentSize = strlen(szContent);
        size_t sEndDataSize = strlen(szEndData);
    
        INTERNET_BUFFERS bufferIn = {};
        bufferIn.dwStructSize  = sizeof(INTERNET_BUFFERS);
        bufferIn.dwBufferTotal = sContentSize + dwFileSize + sEndDataSize;
    
        if (!HttpSendRequestEx(hreq.get(), &bufferIn, NULL, HSR_INITIATE, 0))
        {
            std::cerr << "HttpSendRequestEx Error" << std::endl;
            return;
        }
    
        if (!WriteToInternet(hreq.get(), szContent, sContentSize)))
        {
            std::cerr << "InternetWriteFile Error" << std::endl;
            return;
        }
    
        char szData[BUFSIZE];
        DWORD dw = 0, dwBytes;
    
        while (dw < dwFileSize)
        {
            if (!ReadFile(hIn.get(), szData, std::min(dwFileSize-dw, sizeof(szData)), &dwBytes, NULL))
            {
                std::cerr << "ReadFile Error" << std::endl;
                return;
            }
    
            if (!WriteToInternet(hreq.get(), szData, dwBytes))
            {
                std::cerr << "InternetWriteFile Error" << std::endl;
                return;
            }
    
            dw += dwBytes;
        }
    
        if (!WriteToInternet(hreq.get(), szEndData, sEndDataSize))
        {
            std::cerr << "InternetWriteFile Error" << std::endl;
            return;
        }
    
        if (!HttpEndRequest(hreq.get(), NULL, HSR_INITIATE, 0))
            std::cerr << "HttpEndRequest Error" << std::endl;
    }
    

    【讨论】:

    • 能否请您重写 szContent 和 szEndData,以便我能很好理解,谢谢
    • @YeniiCeri:你不明白什么?无论您在 Content-Type 标头的 boundary 中指定什么,只需在内容数据的每个边界线中在其前面加上 --
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    相关资源
    最近更新 更多