5

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.

4
  • It looks like the PNG file contains a NULL character. printf's '%s' prints a char string until it encounters NULL and stops. Try using C++ streams instead: cout << mystring; Commented May 26, 2013 at 17:58
  • You should consider using std::ifstream and std::ostream rather than the C stream I/O calls. Commented May 26, 2013 at 18:23
  • This is almost not a C++ but C. Turning to C++ makes things simpler. As the answer below shows. Hacking your way to the solution is definitely one "wrong path". Commented Dec 31, 2018 at 11:37
  • There is no need to read the entire file into memory, and certainly no need to convert it from a byte array to any other format. Commented Dec 25, 2019 at 1:00

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

\0 is only treated specially when converting from const char*, and no length is available.
Ah yes, thank you. I've updated it to remove the reference to string and updated the description
Umm.. Then how should I convert the data in vector to string? I've tried string binary_str(buffer.begin(), buffer.end());, but still, the string is same \211PNG :(
I've updated my answer to include the use of fwrite and a link on how to change stdout so that it works in binary mode instead of text to prevent potential mangling of newline characters.
0

First, you need to open the file(where your picture is) to read binary.

fp=fopen(filename,"rb");

Next,set stdout to binary mode with this command:

 _setmode(_fileno(stdout),O_BINARY);

You need to include <fcntl.h> and <io.h> headers.Find the exact size of the picture you need to send,for example like Captain Obvilous has written:

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

Now use fread function to read all bytes from the picture:

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

Variable ch is type char.When you are finish set file mode back:

_setmode(_fileno(stdout), _O_TEXT);

**NOTE:**This code was written mostly in C but you can easily read files in C++ using istream

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.