【问题标题】:Convert server response (std::string) into a png file将服务器响应(std::string)转换为 png 文件
【发布时间】:2011-10-10 10:47:40
【问题描述】:

我正在使用 boost::asio 收到来自服务器的响应。结果存储在 std::string 中。

我想将此 std::string 转换为 .png 图像并将其写入文件。

我在这方面遇到了严重的麻烦。这是我的代码:

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

write_to_binary_file(response.data());

,其中 write_to_binary_file 是:

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

现在,如果我对我的 C++ 程序编写的文件进行八进制转储,它与我直接从 URL 下载文件时得到的八进制转储完全不同。


更新了 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;
}

使用int x=write_to_binary_file(response.c_str());拨打电话

还是不适合我 ;(

【问题讨论】:

  • errorBuffer 的声明和curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer); 行没有意义。重新阅读该功能的手册。
  • sizeof(std::string) 大错特错。您是否使用 reinterpret_cast 是因为编译器就像“哦,上帝,不要将 p_Data 转换为那个”?
  • @todda.speot.is 是的,我把它从互联网上撤下来了……你能就如何解决问题提出建议吗?
  • 什么是“响应”?你为什么要把它转换成字符串?
  • 嗨亚历山德罗 - “响应”是一个 std::string

标签: c++ string image curl png


【解决方案1】:
binary_file.write(reinterpret_cast<const char*>(&p_Data),sizeof(std::string));

没错,这就是为什么reinterpret_cast 在很多场景下都很差的原因:它促进了guesscoding

这是当你扔掉你的参考资料,只是假设你需要在某处传递一个指向std::string 的指针,并确定编译器的错误消息是错误的。所以,你使用reinterpret_cast 来“闭嘴”,现在你想知道为什么没有任何工作正常。

大概有人告诉你“使用std::string”而不是char数组,而你只是交换了类型名称,而不是对实际的区别进行任何研究。 p>

std::string 是一个具有各种内部成员的对象,通常间接存储其实际的字符串数据(动态地,或者您可能不准确和误导性地称为“在堆上”);事实上,关于它如何存储数据,它已经完全从你那里抽象出来了。无论哪种方式,它都不仅仅是一个 char 数组。

使用它提供的 API 获取指向 char 数组的指针,可以使用 std::string::data()std::string::c_str() ... 和 stop guesscoding


另外,我怀疑这个编译:

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

你可能想构造一个具有一定长度的std::string

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

字符串是对象,而不是原始数组!

【讨论】:

  • 您好,感谢您的回复。是的,我对猜测编码感到内疚,但我有点 C++ 菜鸟 tbqh...我已经用更新的代码编辑了我的帖子...
  • @Eamorr:我的回答成立。您更新的代码不会改变它。
  • 嗨 Tomalak,感谢您回复我。我又更新了OP。如果我直接下载文件,img.png 的八进制转储与八进制转储不同。我自己根本无法解决这个问题。
  • @Eamorr:如果您在迭代调试支持之后,我认为聊天室更适合解决这个问题。我已经列出了上面的事实,这些事实应该会给你一个很好的起点。不幸的是,您仍然std::string 视为一个数组,完全忽略了它为您提供的API 函数。 file.write(p_Data,sizeof(p_Data)); 很接近。你在用哪本 C++ 书?
  • @Eamorr:取而代之的是,从this list of recommended reading 那里得到一本书,在你手头之前不要再写一行代码。
【解决方案2】:

我的第一个猜测是“reinterpret_cast(&p_Data)” 正在弄乱你的数据。你为什么要这样投? p_Data.data() 或 p_Data.c_str() 可能是你想要的。

【讨论】:

    【解决方案3】:

    您可能要考虑使用SFML 来完成这样的任务,因为它是deals with HTTP 并大大简化了任务。我个人使用它从网站下载 .GIF 图片。

    为了解决您的问题(我承认我不熟悉 boost::asio),您需要考虑的事情是:

    1. 不要做 sizeof(std::string)。 Std::string 有一个 .size() 方法。所以 response.size();在这里适用。

    2. 在处理来自网站的原始数据时,不要使用 strlen()、sizeof() 或任何使用空终止标记值的方法,因为空值可能出现在任何地方 em> 在原始数据流中(它不像文本)。使用 std::string 的 .size() 方法。

    3. 鉴于它是原始数据,以二进制模式写入!文本模式将在 windows 上文件中的 ctrl+z 信号终止。 IE 它可能不会完全写入文件。

    4. Base64 并不总是适用。无论如何,这很有可能是自动化的。

    5. 考虑从提供的 URL 本身中提取文件名。

    假设响应是可以接受的,这就是你想要的代码:

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

    【讨论】:

      【解决方案4】:

      看看你的响应字符串的内容是什么。 可能你应该在写入文件之前通过 base64 解码器对其进行解码

      【讨论】:

      • 您好,非常感谢您的回复。 base64 转换真的有必要吗?我将响应字符串打印到控制台,它看起来像一个普通的 PNG 图像。你能告诉我如何解码成base64吗?非常感谢,
      • 不是“进入”而是“来自”。通过 http 传输的二进制数据被编码为 base64 以避免使用不可打印的字符。这是http的限制。但我不知道 Curl 是否会自行解码。可能不会,如果它使用 std::string 来处理响应
      • 你知道有什么办法可以解决这个问题吗?
      • 我之前的编辑没有保存。在此处查找解码器的来源adp-gmbh.ch/cpp/common/base64.html
      猜你喜欢
      • 2016-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-22
      相关资源
      最近更新 更多