Convert server response (std::string) into a png f

2019-09-02 09:07发布

I'm getting a response from a server using boost::asio. The result is stored in a std::string.

I want to convert this std::string into a .png image and write it to file.

I'm having awful trouble with this. Here is my code:

CURL *curl;
CURLcode res;
std::string errorBuffer[CURL_ERROR_SIZE];
std::string url="http://www.google.ie/images/srpr/logo3w.png";
std::string response="";

curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_string);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

res=curl_easy_perform(curl);

curl_easy_cleanup(curl);

response now stored in response

write_to_binary_file(response.data());

, where write_to_binary_file is:

void write_to_binary_file(std::string p_Data){
        //std::fstream binary_file("./img.png",std::ios::out|std::ios::binary|std::ios::app);
        //binary_file.write(reinterpret_cast<const char*>(&p_Data),sizeof(std::string));
        //binary_file.close();
        std::ofstream file("img.png", std::ios::binary);
        file.write(p_Data,sizeof(p_Data));
        file.close();
}

Now if I do an octal dump on the file written by my C++ program, it's totally different to the octal dump I get when I download the file from the URL directly.


Updated write_to_binary_file

int write_to_binary_file(const char* p_Data){
        //std::fstream binary_file("./img.png",std::ios::out|std::ios::binary|std::ios::app);
        //binary_file.write(reinterpret_cast<const char*>(&p_Data),sizeof(std::string));
        //binary_file.write(c_str(),sizeof(ring));
        //binary_file.close();
        /*std::ofstream file("img.png", std::ios::binary);
        file.write(p_Data,len);
        file.close();*/
        FILE *fp;
        size_t count;
        const char *str = p_Data;

        fp = fopen("img.png", "w");
        if(fp == NULL) {
                perror("failed to open img.png");
                return EXIT_FAILURE;
        }
        count = fwrite(str, 1, strlen(str), fp);
        printf("Wrote %zu bytes. fclose(fp) %s.\n", count, fclose(fp) == 0 ? "succeeded" : "failed");
        return EXIT_SUCCESS;
}

Call using int x=write_to_binary_file(response.c_str());

Still won't work for me ;(

4条回答
爷的心禁止访问
2楼-- · 2019-09-02 09:18

You might want to consider using SFML for such a task, given it deals with HTTP and greatly simplifies the task. I've personally used it download .GIF images from a website.

To address your question (I am admittedly not familiar with boost::asio), the things you will want to consider is:

  1. Don't do sizeof(std::string). Std::string has a .size() method. So response.size(); is applicable here.

  2. Don't use strlen(), sizeof() or any method that uses a null-terminated sentinel value when dealing with raw data from a website, as a null-value can occur anywhere in the raw data stream (it isn't like text). Use std::string's .size() method.

  3. Given it's raw data, write in binary mode! Text mode will terminate on the ctrl+z signal within a file on windows. IE it'd likely not fully write the file.

  4. Base64 doesn't always apply. Chances are this is automated anyway.

  5. Consider extracting the filename from the supplied URL itself.

Assuming response is acceptable, this is the code you want:

FILE * File = NULL;
File = fopen("Image.png","wb"); //Note b for binary mode
if(File == NULL){return 1;} //Error
if(fwrite(response.c_str(),response.size(),1,File) != response.size()) //If there is a mismatch between the amount of data written and the string size, we have an error
{
    perror("File write error occurred"); //Needs errno
    fclose(File);
    return 2;
}

fclose(File);
查看更多
Viruses.
3楼-- · 2019-09-02 09:37
binary_file.write(reinterpret_cast<const char*>(&p_Data),sizeof(std::string));

Right, this is why reinterpret_cast is poor in many scenarios: it promotes guesscoding.

This is when you've thrown away your reference materials and just assumed that you need to pass a pointer to an std::string somewhere, and decided that the compiler's error messages were wrong. So, you used reinterpret_cast to "shut it up", and now you're wondering why nothing's working properly.

Presumably somebody has told you to "use std::string" instead of char arrays, and you've just swapped the type names rather than doing any research into what the actual difference is.

std::string is an object with various internal members, and which usually stores its actual string data indirectly (dynamically, or what you may inaccurately and misleadingly refer to as "on the heap"); in fact, it's completely abstracted away from you as to just how it stores its data. Either way, it's not just a char array.

Use the API that it provides to obtain a pointer to a char array, with either std::string::data() or possibly std::string::c_str() ... and stop guesscoding.


Also, I doubt that this compiles:

std::string errorBuffer[CURL_ERROR_SIZE];
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);

And you probably wanted to construct a std::string with a certain length:

std::string errorBuffer(CURL_ERROR_SIZE);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &errorBuffer[0]);
// ^ (requires C++11 to be completely safe)

Strings are objects, not raw arrays!

查看更多
疯言疯语
4楼-- · 2019-09-02 09:38

My first guess would be that "reinterpret_cast(&p_Data)" is messing up your data. Why do you cast it in this way? p_Data.data() or p_Data.c_str() are probably what you want.

查看更多
爷、活的狠高调
5楼-- · 2019-09-02 09:45

look what is the contents of your responce string. probably you should decode it by base64 decoder before writing into file

查看更多
登录 后发表回答