将服务器响应 (std::string) 转换为 png 文件

发布于 2024-12-09 04:13:49 字数 2378 浏览 0 评论 0原文

我使用 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());

调用,但仍然对我不起作用;(

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

柏拉图鍀咏恒 2024-12-16 04:13:49
binary_file.write(reinterpret_cast<const char*>(&p_Data),sizeof(std::string));

是的,这就是为什么reinterpret_cast在很多场景下表现不佳:它促进了猜测编码

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

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

std::string 是一个具有各种内部成员的对象,通常间接存储其实际字符串数据(动态地,或者您可能不准确且误导性地称为“在堆上”);事实上,它对于如何存储数据来说是完全抽象的。不管怎样,它不仅仅是一个 char 数组。

使用它提供的 API 获取指向 char 数组的指针,使用 std::string::data() 或可能 std::string: :c_str() ...以及停止猜测编码


另外,我怀疑这是否可以编译:

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)

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

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!

冰之心 2024-12-16 04:13:49

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

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.

暗恋未遂 2024-12-16 04:13:49

您可能需要考虑使用 SFML 来完成此类任务,因为 处理 HTTP 并大大简化了任务。我个人曾使用它从网站下载 .GIF 图像。

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

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

  2. 在处理来自网站的原始数据时,不要使用 strlen()、sizeof() 或任何使用 null 终止标记值的方法,因为 null 值可能出现在任何地方原始数据流(它不像文本)。使用 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);

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);
慵挽 2024-12-16 04:13:49

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文