C++ 中的图像 HTTP 上传已损坏
Image HTTP upload in C++ is corrupted
我试图使用 Windows 套接字 HTTP 将 PNG 图像上传到 PHP 脚本。一切似乎都正常,但是当我尝试在 Windows 画图中打开我的图像时,我收到一条错误消息:
This is not a valid bitmap file.
不知道怎么回事。我对 HTTP header 字段有点陌生,所以我猜 header 可能是错误的。可能是什么原因造成的?
C++代码(original C++ pastebin link):
#include <iostream>
#include <winsock2.h>
#include <string>
#include <fstream>
#include "windows.h"
#include "stdio.h"
using namespace std;
#define PORT 80
#define IP "127.0.0.1"
#define HOST "locahost"
#define RECEIVER "/up.php"
#define COMPNAME "compname"
#define PROGRAM "program"
#define FILENAME "file"
#define BOUNDARY "----------boundary"
#define DUMMY_DATA "c2FzYXNhc2FzZGRmZGZkY2Q="
#define DUMMY_FILE "ok.png"
//------------------------------------
string constructBody(string args[2], string file[2]);
string readFile(string fileName);
//------------------------------------
int main() {
// initiate the socket!
SOCKET dataSock;
WSADATA wsaData;
int error = WSAStartup(0x0202, &wsaData);
if (error != 0) {
WSACleanup();
exit(1); // oh shit, this shouldn't happen!
}
// all internets, engage!
SOCKADDR_IN target;
target.sin_family = AF_INET;
target.sin_port = htons(PORT);
target.sin_addr.s_addr = inet_addr(IP);
dataSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (dataSock == INVALID_SOCKET) {
exit(1); // Houston, we have a problem!
}
connect(dataSock, (SOCKADDR*)&target, sizeof(target));
string programNames[5][2] = {{"Browser", "Mozilla"}};
string file[2] = {FILENAME, "Default.txt"};
for (int i = 0; i < 1; i++) {
printf("Sending data for %s\n", (programNames[i][1]).c_str());
string body = constructBody(programNames[i], file);
char header[1024];
sprintf(header, "POST %s HTTP 1.1\r\n"
"Host: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: multipart/form-data; boundary=%s\r\n"
"\r\n", RECEIVER, IP, strlen(body.c_str()), BOUNDARY);
// printf("%s\n\n", header);
int p = send(dataSock, header, strlen(header), 0);
// printf("p == %d\n", p);
int k = send(dataSock, body.c_str(), strlen(body.c_str()), 0);
// printf("k == %d\n", k);
char buff[1024];
recv(dataSock, buff, 1024, 0);
printf("%s\n\n", buff);
}
closesocket(dataSock);
WSACleanup();
}
string readFile(string fileName) {
string fileContents;
ifstream tmp(fileName.c_str());
getline(tmp, fileContents);
tmp.close();
return fileContents;
}
string constructBody(string args[2], string file[2]) {
string body;
string CRLF = "\r\n";
// first we add the args
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(COMPNAME)+"\""+CRLF);
body.append(CRLF);
body.append(args[0]+CRLF);
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(PROGRAM)+"\""+CRLF);
body.append(CRLF);
body.append(args[1]+CRLF);
// now we add the file
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(FILENAME)+"\"; filename=\""+string(DUMMY_FILE)+"\""+CRLF);
body.append("Content-Type: media-type"+CRLF);
body.append(CRLF);
body.append(DUMMY_DATA+CRLF);
body.append("--"+string(BOUNDARY)+"--"+CRLF);
body.append(CRLF);
// printf(body.c_str()); exit(0);
return body;
}
PHP代码(original PHP pastebin link):
<?php
/* ===== CONSTANTS ===== */
$ROOT_DIR = 'FILES';
$COMPUTER_NAME = 'compname';
$PROGRAM = 'program';
$FILENAME = 'file';
$CHUNK_SIZE = 1024;
/* ===================== */
//=====================================
/**
Function that gets current time and formats it into pretty looking date
*/
function makeDate() {
return strftime('%Y-%m-%d, %H.%M');
}
//=====================================
// check here if the parameters are set. If it's not then it's safe to say some one is snooping around...
if (isset($_POST[$COMPUTER_NAME], $_POST[$PROGRAM], $_FILES[$FILENAME])) {
// construct a full path and create it
$fullPath = $ROOT_DIR.'\'.$_POST[$COMPUTER_NAME].'\'.$_POST[$PROGRAM].'\'.makeDate();
mkdir($fullPath, 0777, true);
// move the files and rename them as temporary
$filename = $_FILES[$FILENAME]['name'];
move_uploaded_file(($_FILES[$FILENAME]['tmp_name']), $fullPath.'\'.$filename.'.tmp');
// decode received files
$src = fopen($fullPath.'\'.$filename.'.tmp', 'rb');
$dst = fopen($fullPath.'\'.$filename, 'wb');
while (!feof($src)) {
fwrite($dst, base64_decode(fread($src, $CHUNK_SIZE)));
}
fclose($dst);
fclose($src);
unlink($fullPath.'\'.$filename.'.tmp'); // remove the temp file after decoding it
echo 'OK!';
} else {
echo 'oh no :(';
}
//=====================================
?>
至少,C++ 代码将 .png 文件作为文本文件读取,并将内容视为以 null 结尾的字符串。它似乎也只读到第一行结尾。您要做的是对 .png 文件进行二进制读取。
尝试更改您的 readFile()
函数以使用与此 example.
相同的二进制读取功能
请注意,在示例中,首先读取文件大小,以便分配足够大小的缓冲区。您可能不想手动处理缓冲区管理,因此您可以选择使用 vector<char>
,相关的内存资源将为您提供 allocated/released。有关 vector<>
.
的参考指南,请参阅此 link
这样的东西可能更接近你想要的:
bool readFile(string fileName, vector<char> &fileContents) {
vector<char> fileContents;
std::ifstream is (fileName.c_str(), std::ifstream::binary);
bool success = is;
if (success) {
// get length of file:
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
fileContents.resize(length);
is.read(fileContents, length);
is.close();
}
return success;
}
有了这些变化,您将不得不改变您的称呼方式 readFile()
。有趣的是 readFile()
从未在您发布的代码中实际调用,因此 constructBody()
中有一个点您声明一个 fileContents
变量并调用 readFile()
填充它。
考虑 readFile()
的这种用法:
vector<char> fileContents;
bool success = readFile(filename, fileContents);
稍后在同一函数中,您需要将 fileContents
输出到 body
缓冲区。
与 fileContents
一样,body
变量不应该是 string
,以避免意外的 null 终止。最好在这种情况下也使用 vector<char>
,并且需要更新对 body.append()
的所有调用以正确附加内容和行尾。它应该很简单,但仍需要一些额外的工作才能完成。
我没有深入研究 PHP 代码,因为这将是您在读取和发送 .png 文件时遇到的第一个主要问题。不管是别的什么,完全。您需要确保 PHP 代码接收到的文件内容与原始 .png 文件的实际内容相匹配,您可以提供调试输出来做到这一点。在它们匹配之前,它可能仍然是 C++ 代码中的一个问题。
我试图使用 Windows 套接字 HTTP 将 PNG 图像上传到 PHP 脚本。一切似乎都正常,但是当我尝试在 Windows 画图中打开我的图像时,我收到一条错误消息:
This is not a valid bitmap file.
不知道怎么回事。我对 HTTP header 字段有点陌生,所以我猜 header 可能是错误的。可能是什么原因造成的?
C++代码(original C++ pastebin link):
#include <iostream>
#include <winsock2.h>
#include <string>
#include <fstream>
#include "windows.h"
#include "stdio.h"
using namespace std;
#define PORT 80
#define IP "127.0.0.1"
#define HOST "locahost"
#define RECEIVER "/up.php"
#define COMPNAME "compname"
#define PROGRAM "program"
#define FILENAME "file"
#define BOUNDARY "----------boundary"
#define DUMMY_DATA "c2FzYXNhc2FzZGRmZGZkY2Q="
#define DUMMY_FILE "ok.png"
//------------------------------------
string constructBody(string args[2], string file[2]);
string readFile(string fileName);
//------------------------------------
int main() {
// initiate the socket!
SOCKET dataSock;
WSADATA wsaData;
int error = WSAStartup(0x0202, &wsaData);
if (error != 0) {
WSACleanup();
exit(1); // oh shit, this shouldn't happen!
}
// all internets, engage!
SOCKADDR_IN target;
target.sin_family = AF_INET;
target.sin_port = htons(PORT);
target.sin_addr.s_addr = inet_addr(IP);
dataSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (dataSock == INVALID_SOCKET) {
exit(1); // Houston, we have a problem!
}
connect(dataSock, (SOCKADDR*)&target, sizeof(target));
string programNames[5][2] = {{"Browser", "Mozilla"}};
string file[2] = {FILENAME, "Default.txt"};
for (int i = 0; i < 1; i++) {
printf("Sending data for %s\n", (programNames[i][1]).c_str());
string body = constructBody(programNames[i], file);
char header[1024];
sprintf(header, "POST %s HTTP 1.1\r\n"
"Host: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: multipart/form-data; boundary=%s\r\n"
"\r\n", RECEIVER, IP, strlen(body.c_str()), BOUNDARY);
// printf("%s\n\n", header);
int p = send(dataSock, header, strlen(header), 0);
// printf("p == %d\n", p);
int k = send(dataSock, body.c_str(), strlen(body.c_str()), 0);
// printf("k == %d\n", k);
char buff[1024];
recv(dataSock, buff, 1024, 0);
printf("%s\n\n", buff);
}
closesocket(dataSock);
WSACleanup();
}
string readFile(string fileName) {
string fileContents;
ifstream tmp(fileName.c_str());
getline(tmp, fileContents);
tmp.close();
return fileContents;
}
string constructBody(string args[2], string file[2]) {
string body;
string CRLF = "\r\n";
// first we add the args
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(COMPNAME)+"\""+CRLF);
body.append(CRLF);
body.append(args[0]+CRLF);
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(PROGRAM)+"\""+CRLF);
body.append(CRLF);
body.append(args[1]+CRLF);
// now we add the file
body.append("--"+string(BOUNDARY)+CRLF);
body.append("Content-Disposition: form-data; name=\""+string(FILENAME)+"\"; filename=\""+string(DUMMY_FILE)+"\""+CRLF);
body.append("Content-Type: media-type"+CRLF);
body.append(CRLF);
body.append(DUMMY_DATA+CRLF);
body.append("--"+string(BOUNDARY)+"--"+CRLF);
body.append(CRLF);
// printf(body.c_str()); exit(0);
return body;
}
PHP代码(original PHP pastebin link):
<?php
/* ===== CONSTANTS ===== */
$ROOT_DIR = 'FILES';
$COMPUTER_NAME = 'compname';
$PROGRAM = 'program';
$FILENAME = 'file';
$CHUNK_SIZE = 1024;
/* ===================== */
//=====================================
/**
Function that gets current time and formats it into pretty looking date
*/
function makeDate() {
return strftime('%Y-%m-%d, %H.%M');
}
//=====================================
// check here if the parameters are set. If it's not then it's safe to say some one is snooping around...
if (isset($_POST[$COMPUTER_NAME], $_POST[$PROGRAM], $_FILES[$FILENAME])) {
// construct a full path and create it
$fullPath = $ROOT_DIR.'\'.$_POST[$COMPUTER_NAME].'\'.$_POST[$PROGRAM].'\'.makeDate();
mkdir($fullPath, 0777, true);
// move the files and rename them as temporary
$filename = $_FILES[$FILENAME]['name'];
move_uploaded_file(($_FILES[$FILENAME]['tmp_name']), $fullPath.'\'.$filename.'.tmp');
// decode received files
$src = fopen($fullPath.'\'.$filename.'.tmp', 'rb');
$dst = fopen($fullPath.'\'.$filename, 'wb');
while (!feof($src)) {
fwrite($dst, base64_decode(fread($src, $CHUNK_SIZE)));
}
fclose($dst);
fclose($src);
unlink($fullPath.'\'.$filename.'.tmp'); // remove the temp file after decoding it
echo 'OK!';
} else {
echo 'oh no :(';
}
//=====================================
?>
至少,C++ 代码将 .png 文件作为文本文件读取,并将内容视为以 null 结尾的字符串。它似乎也只读到第一行结尾。您要做的是对 .png 文件进行二进制读取。
尝试更改您的 readFile()
函数以使用与此 example.
请注意,在示例中,首先读取文件大小,以便分配足够大小的缓冲区。您可能不想手动处理缓冲区管理,因此您可以选择使用 vector<char>
,相关的内存资源将为您提供 allocated/released。有关 vector<>
.
这样的东西可能更接近你想要的:
bool readFile(string fileName, vector<char> &fileContents) {
vector<char> fileContents;
std::ifstream is (fileName.c_str(), std::ifstream::binary);
bool success = is;
if (success) {
// get length of file:
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
fileContents.resize(length);
is.read(fileContents, length);
is.close();
}
return success;
}
有了这些变化,您将不得不改变您的称呼方式 readFile()
。有趣的是 readFile()
从未在您发布的代码中实际调用,因此 constructBody()
中有一个点您声明一个 fileContents
变量并调用 readFile()
填充它。
考虑 readFile()
的这种用法:
vector<char> fileContents;
bool success = readFile(filename, fileContents);
稍后在同一函数中,您需要将 fileContents
输出到 body
缓冲区。
与 fileContents
一样,body
变量不应该是 string
,以避免意外的 null 终止。最好在这种情况下也使用 vector<char>
,并且需要更新对 body.append()
的所有调用以正确附加内容和行尾。它应该很简单,但仍需要一些额外的工作才能完成。
我没有深入研究 PHP 代码,因为这将是您在读取和发送 .png 文件时遇到的第一个主要问题。不管是别的什么,完全。您需要确保 PHP 代码接收到的文件内容与原始 .png 文件的实际内容相匹配,您可以提供调试输出来做到这一点。在它们匹配之前,它可能仍然是 C++ 代码中的一个问题。