我写了一个C ++函数来使用WinInet Library执行上传文件。编译和执行代码时没有返回错误,但我无法在服务器端处理该文件。 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;
}
答案 0 :(得分:2)
szContent
和szEndData
中的MIME边界线是错误的。阅读RFC 2046。内容数据中的边界线以两个-
个字符开头,后跟boundary
标题的Content-Type
参数中指定的文本(如果是结束边界线,则为接着又增加了两个-
个字符。
您在内容数据中使用的边界线缺少这两个前导-
个字符。您在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;
}