用C++通过HTTP发送图像

5
我将尝试使用C++实现一个简单的HTTP服务器。我成功地向浏览器发送了文本响应,但是我不能发送二进制文件请求的响应。 以下是我获取HTML响应PNG文件请求的代码:
string create_html_output_for_binary(const string &full_path)
{
    const char* file_name = full_path.c_str();

    FILE* file_stream = fopen(file_name, "rb");

    string file_str;

    size_t file_size;

    if(file_stream != nullptr)
    {
        fseek(file_stream, 0, SEEK_END);

        long file_length = ftell(file_stream);

        rewind(file_stream);

        // Allocate memory. Info: http://www.cplusplus.com/reference/cstdio/fread/?kw=fread
        char* buffer = (char*) malloc(sizeof(char) * file_length);

        if(buffer != nullptr)
        {
            file_size = fread(buffer, 1, file_length, file_stream);

            stringstream out;

            for(int i = 0; i < file_size; i++)
            {
                out << buffer[i];
            }

            string copy = out.str();

            file_str = copy;
        }
        else
        {
            printf("buffer is null!");
        }
    }
    else
    {
        printf("file_stream is null! file name -> %s\n", file_name);
    }

    string html = "HTTP/1.1 200 Okay\r\nContent-Type: text/html; charset=ISO-8859-4 \r\n\r\n" + string("FILE NOT FOUND!!");

    if(file_str.length() > 0)
    {
        // HTTP/1.0 200 OK
        // Server: cchttpd/0.1.0
        // Content-Type: image/gif
        // Content-Transfer-Encoding: binary
        // Content-Length: 41758

        string file_size_str = to_string(file_str.length());

        html = "HTTP/1.1 200 Okay\r\nContent-Type: image/png; Content-Transfer-Encoding: binary; Content-Length: " + file_size_str + ";charset=ISO-8859-4 \r\n\r\n" + file_str;

        printf("\n\nHTML -> %s\n\nfile_str -> %ld\n\n\n", html.c_str(), file_str.length());
    }

    return html;
}

这段代码成功读取文件并将数据存储在char* buffer中。 让我困惑的是,file_str总是包含\211PNG,尽管当我检查其大小时,它比\211PNG要大得多。 我怀疑这是导致我的图像无法在浏览器中加载的问题,因为当我打印HTML时,它只显示:

HTTP/1.1 200 Okay
Content-Type: image/png; Content-Transfer-Encoding: binary; Content-Length: 187542;charset=ISO-8859-4 

\211PNG

我认为将二进制数据发送到浏览器的方式与发送文本数据的方式相同,因此我首先创建字符串头,然后读取文件数据,将其转换为字符串并与头部组合,最后将一个大的单个字符串发送到HTTP套接字。

我也尝试了以下代码:

if (file_stream != NULL)
{
    short stringlength = 6;

    string mystring;

    mystring.reserve(stringlength);

    fseek(file_stream , 0, SEEK_SET);
    fread(&mystring[0], sizeof(char), (size_t)stringlength, file_stream);

    printf("TEST -> %s, length -> %ld\n", mystring.c_str(), mystring.length());

    fclose(file_stream );
}

但是HTML输出始终相同,而且在printf中mystring也只包含\211PNG

我走错了吗? 请帮助找出我的代码中的错误。谢谢。


看起来PNG文件包含一个NULL字符。printf的'%s'打印一个char字符串,直到遇到NULL并停止。尝试使用C++流代替:cout << mystring; - nishantjr
你应该考虑使用std::ifstreamstd::ostream而不是C流I/O调用。 - Captain Obvlious
这几乎不是C++而是C。转向C++会使事情变得更简单,正如下面的答案所示。通过黑客方式解决问题绝对是一条“错误的道路”。 - user10133158
没有必要将整个文件读入内存,当然也不需要将其从字节数组转换为任何其他格式。 - user207421
2个回答

3

您将数据存储在一个大块中,放入了std::stringstream中。这样做是行不通的,因为值0会被解释为空终止符。这会导致空终止符后面的所有内容都被忽略。您应该使用像std::vector这样的容器来存储和管理二进制数据。

#include <vector>

string create_html_output_for_binary(const string &full_path)
{
    std::vector<char> buffer;

    //... other code here

    if(ile_stream != nullptr)
    {
        fseek(file_stream, 0, SEEK_END);
        long file_length = ftell(file_stream);
        rewind(file_stream);

        buffer.resize(file_length);

        file_size = fread(&buffer[0], 1, file_length, file_stream);
    }

    // .... other code here
}

为了输出数据,请不要使用printf。它可能会以不同的方式处理换行符,并在遇到第一个空终止符时停止。相反(保持使用 C 流 IO),请使用fwrite
fwrite (buffer.data(), 1 , buffer.size() , stdout );

为了让上述内容运行起来,您需要重新打开stdout以使用二进制模式进行写入。这里在Stackoverflow上的这个答案展示了如何实现。这只是将内容输出到stdout。由于您正在通过套接字传输数据,因此无需对stdout进行任何操作。

1
当从const char*进行转换且没有长度可用时,“\0”才会被特殊处理。 - MSalters
啊,好的,谢谢。我已经更新了它,删除了对“string”的引用,并更新了描述。 - Captain Obvlious
那么,我应该如何将向量中的数据转换为字符串呢?我尝试过 string binary_str(buffer.begin(), buffer.end());,但是仍然得到了相同的字符串 \211PNG :( - yunhasnawa
我已更新我的答案,包括使用fwrite的方法以及一个链接,介绍如何将stdout更改为二进制模式,而不是文本模式,以防止换行符被搞乱。 - Captain Obvlious

0

首先,您需要打开文件(图片所在的文件)以进行二进制读取。

fp=fopen(filename,"rb");

接下来,使用以下命令将标准输出设置为二进制模式:

 _setmode(_fileno(stdout),O_BINARY);

你需要包含 <fcntl.h><io.h> 头文件。找到你需要发送的图片的确切大小,例如像Captain Obvilous所写:

  fseek(fp, 0L, SEEK_END);
long file_length = ftell(fp);
rewind(fp);

现在使用fread函数读取图片的所有字节:

    while (!feof(fp))
  {
      fread(&ch, 1, 1, fp);
      cout << ch;
  }

变量ch的类型是char。当你完成后,请将文件模式设置回来:

_setmode(_fileno(stdout), _O_TEXT);

**注意:**这段代码主要是用C语言编写的,但你也可以使用istream在C++中轻松读取文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接