Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Sending Image via HTTP

Tags:

c++

http

file-io

I try to implement a simple HTTP server using C++. I was able to send text response to browser, but I am failing to send response for binary file request.

Here is my code to get HTML response to PNG file request:

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

This code successfully read file and store data on char* buffer. What makes me confuse is the file_str always contains \211PNG, although when I check its size, is much large than \211PNG. I suspect this is the problem that cause my image does not loaded in browsers because when I printf the html, it only shows:

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

\211PNG

What I am thinking is the way to send binary data to browser is same with sending text data, so I make the string header first, then read file data, convert it to string and combine with the header, then finally send a large single string to HTTP socket.

I also tried this code:

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

But the HTML output always the same, and mystring also contains only \211PNG when printf-ed.

Am I in the wrong path? Please help find out the mistakes in my code. Thank you.

like image 388
yunhasnawa Avatar asked Oct 21 '22 09:10

yunhasnawa


1 Answers

You are storing the data in one large chunk into a std::stringstream. That will not work as the value zero will be interpreted as a null terminator. This causes everything after the null terminator to be ignored. You should use a container like std::vector to store and manage binary data.

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

To output the data do not use printf. It may handle new lines differently and will stop at the first null terminator is encounters. Instead (keeping with your use of C stream IO) use fwrite.

fwrite (buffer.data(), 1 , buffer.size() , stdout );

In order for the above to work you will need to reopen the stdout to that it writes in binary mode. This answer here on Stackoverflow shows how to accomplish that. This is just to output the contents to the stdout Since you are transmitting the date over sockets you do not have to do anything to stdout.

like image 197
Captain Obvlious Avatar answered Oct 24 '22 09:10

Captain Obvlious