首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WinInet上传文件

WinInet上传文件
EN

Stack Overflow用户
提问于 2017-12-19 00:51:12
回答 1查看 1.7K关注 0票数 1

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

代码语言:javascript
复制
#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;
}
EN

回答 1

Stack Overflow用户

发布于 2017-12-19 01:18:24

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

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

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

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

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

尝试更像这样的东西:

代码语言:javascript
复制
#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()来在每个循环迭代中以块的形式对文件进行read+send。这样,在发送以下文件之前,不必将整个文件读入内存:

代码语言:javascript
复制
#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;
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47878566

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档