Libcurl: image在使用fstream::write而不是fwrite时被损坏

Libcurl: image is damaged when using fstream::write instead of fwrite

本文关键字:fwrite 损坏 write image fstream Libcurl      更新时间:2023-10-16

我正在尝试从url下载图像文件。我以fwrite为例,取得了成功。现在我试图使用fstream::write来保存数据(ios::binary),但是数据损坏。下面是我的代码:

#include"stdafx.h"
#include<fstream>
#include<iostream>
#include <curl/curl.h>
#include <string.h>
using namespace std;
size_t write_data(void *ptr, size_t size, size_t nmemb, char* out) {
    //void *ptr, size_t size, size_t nmemb, File* fp
   
    fstream file;
    if (file.is_open()){
        file.close();
        file.clear();
    }
    file.open(out, ios::out | ios::binary);
    if (file.is_open()){
        cout << "open successfullyn" << endl;
        
        file.write((char*)ptr, nmemb*size);  // Does it correct?
    };
    // fwrite(ptr,size,nmemb,fp);
    file.close();
    file.clear();
    cout <<"n sizeof(ptr): " << sizeof(ptr) //size of ptr[0]?
         <<"n sizeof(char): " << sizeof(char)
         <<"n size: " << size
         <<"n nmemb: " << nmemb<< endl;
         return size*nmemb;
}

我对write_data中的参数感到困惑。根据CURLOPT_WRITEFUNCTION

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);

"ptr指向已传递的数据,该数据的大小为size乘以nmemb。"

……那么size和nmemb的含义是什么呢?

当尝试从网站下载数据时,我打印了前3个参数。似乎char*ptr是数据存储的内存地址(作为'char a[]'?), size是元素的大小,nmemb是元素的数量。所以数据大小= size * nmemb。我说的对吗?

输出也很混乱:

open successfully
sizeof(ptr):4
sizeof(char):1
size:1
nmemb:2715 
open successfully
sizeof(ptr):4
sizeof(char):1
size:1
nmemb:4865
download successfully

当下载相同的url时,nmemb和文件的打开时间经常改变。

我也对'sizeof(ptr)'感到困惑,它返回'4'(int的大小?)。我如何使用'sizeof'来获得数据内存的大小,以便我可以证明数据大小是'size * nmemb'?

CURLcode download(char* url,char* out){
    CURL *curl = NULL;
    //FILE *fp = NULL;
    CURLcode res;
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, out);    //fp
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        return res;
    }
    else
    {
        return CURLE_FAILED_INIT;
    }
}    
int main()
{
    CURLcode res = download("http://XXXXXX.gif", "D:\test.gif");
    if (CURLE_OK == res)
        cout << "download successfully.n" << endl;
    else
        cout<<"cannot download.n"<<endl;
    return 0;
}

谢谢!:)

每个文件可以多次回调。你不应该在每次函数被调用时都创建一个新的文件流——你应该使用user data参数传递它。否则,您将继续覆盖文件开头的数据。

下面是一个示例实现:

size_t write_data(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    std::ofstream *out = static_cast<std::ofstream *>(userdata);
    size_t nbytes = size * nmemb;
    out->write(ptr, nbytes);
    return nbytes;
}

您还需要使用参数CURLOPT_WRITEDATA调整对curl_easy_setopt的调用,以便实际传递文件流。确保流在函数运行时不会超出作用域!

CURLcode download(char* url, char* out) {
    CURL *curl = NULL;
    std::ofstream output(out, ios::binary);
    CURLcode res;
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &output);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        return res;
    }
    else
    {
        return CURLE_FAILED_INIT;
    }
}