图像在C ++中的HTTP上传已损坏

时间:2016-04-29 16:22:16

标签: php c++ http winsock

我试图使用Windows套接字HTTP将PNG图像上传到PHP脚本。一切似乎工作正常,但当我尝试在Windows Paint中打开我的图像时,我收到一条错误消息:

  

这不是有效的位图文件。

我不知道出了什么问题。我对HTTP标头字段有点新,所以我猜我的标题可能是错误的。可能导致这种情况的原因是什么?

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 :(';
}
//=====================================
?>

1 个答案:

答案 0 :(得分:0)

至少,C ++代码将.png文件作为文本文件读取,并将内容视为以null结尾的字符串。它似乎只读到第一行结束。你想要做的是.png文件的二进制读取。

尝试更改readFile()功能,以使用与此example相同的二进制读取功能。

请注意,在示例中,首先读取文件大小,以便分配足够大小的缓冲区。您可能不想手动处理缓冲区管理,因此您可以选择使用vector<char>,并为您分配/释放相关的内存资源。有关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,以避免偶然的空终止。在这个实例中使用vector<char>也会更好,并且需要更新对body.append()的所有调用以正确附加内容和行结尾。它应该是直截了当的,但仍需要一些额外的工作才能完成。

我还没有深入研究PHP代码,因为这将是您阅读和发送.png文件时遇到的第一个主要问题。它是否完全不同于其他东西。您需要确保PHP代码接收的文件内容与原始.png文件的实际内容相匹配,并且您可以提供调试输出来执行此操作。在它们匹配之前,它可能仍然是C ++代码中的一个问题。