2017-12-19 19 views
0

WinInet 라이브러리를 사용하여 파일 업로드를 수행하는 C++ 함수를 작성했습니다. 코드는 오류를 반환하지 않고 컴파일되고 실행되지만 서버 측에서 파일을 처리 할 수 ​​없습니다. PHP와 파이썬 서버 모두 파일을 인식하지 못합니다. szContentszEndDataWinInet 업로드 파일

#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; 
} 

답변

2

당신의 MIME 경계선이 잘못 : 다음은 코드입니다. RFC 2046을 읽으십시오. 콘텐츠 데이터의 경계선은 - 문자로 시작하고 헤더의 boundary 매개 변수 (끝 경계선의 경우 그 다음에 두 개 이상, - 자)가 지정된 텍스트로 시작됩니다.

콘텐츠 데이터에서 사용중인 경계선에 - 문자가 2 개 있습니다. boundary 헤더에 27 개의 최상위 대시가 있으므로 콘텐츠에 29 개의 대시가 필요하며 그렇지 않은 경우 27 개가 필요합니다.

또한 입력 파일의 크기가 정확히 1023 바이트라고 가정합니다. 그러나 더 중요한 것은 CreateFile() 호출에서 오류 처리를 수행하지 않아 파일이 실제로 열려 있는지 또는 ReadFile() 호출에서 오류 처리가 수행되어 데이터가 실제로 읽혀지고 있는지 확인합니다 (힌트 : 네 번째 매개 변수 은 5 번째 매개 변수가 NULL 일 때 NULL입니다.)

전체 파일 (음, 최대 1023 바이트, 어쨌든)을 메모리로 읽은 다음 보내려고합니다. 적어도 실제 파일 크기를 얻으려면 GetFileSize()을 사용하고 그에 따라 szData을 할당하고 직접 읽어 들여야합니다. szBuffer은 전혀 필요하지 않습니다.

코드에 다른 사소한 오류가 있습니다. 더이 대신 같은 것을 시도 등

, 메모리 누수, sizeof()을 오용 파일 데이터가에에 null 바이트를 포함하지 않는 가정처럼 :

또 다른 옵션 대신 HttpSendRequestEx()을 사용하는 것입니다
#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; 
} 

, 그래서 루프 내에서 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; 
} 
+0

당신이 szContent 및 szEndData를 다시 주실 래요, 그래서 잘 이해할 수 있고, 당신에게 –

+0

@YeniiCeri 감사합니다 : 당신이 정확히 무엇을 이해하지 못하는 ? 'Content-Type' 헤더의 '경계'에 무엇을 지정 하든지 콘텐츠 데이터의 각 경계선 앞에'--'를두기 만하면됩니다. –