2015-05-23 2 views
2

Я пытаюсь загрузить файл изображения с URL-адреса. Я следовал примеру, используя fwrite, и это удалось. Сейчас я пытаюсь использовать fstream::write, чтобы сохранить данные (ios::binary), но данные повреждены. Вот мой код:Libcurl: изображение повреждено при использовании fstream :: write вместо fwrite

#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 successfully\n" << 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."

...... так какие значения имеют размер и nmemb?

При попытке загрузить данные с веб-сайта я напечатал первые 3 параметра. Кажется, что char*ptr является адресом памяти, который хранит данные (как «char a [] '?), А size - это размер элемента, nmemb - это количество элементов. Таким образом, размер данных = размер * 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' для получения размера памяти данных, чтобы я мог доказательство, что размер данных «размер * 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; 
} 

Спасибо! :)

+3

В случае, если вы не заметили, ваша функция записи для вашего первого списка кодов всегда записывает свои данные в * начало * файла. Если ваша функция не вызывается только один раз, я не удивлюсь, что она не совпадает с завихрением для файла IO для вас. – WhozCraig

+0

Есть еще одна вещь, которую я заметил. Вы создаете 'fstream', который затем используется только для записи. Как насчет использования 'ofstream' вместо этого, который статически (т. Е. Во время компиляции) позволяет вам использовать его только для этого? Кроме того, вы проверяете, открыт ли поток, что совершенно невозможно! Просто используйте 'outstream-файл (out, ios_base :: binary);'.Позже вы явно закрываете() 'it и' clear() 'streamstate, что избыточно, потому что после этого вы не используете поток. Вместо этого, 'flush()' поток, а затем проверить streamstate для проверки ошибок. Все это не объясняет ваши проблемы. –

+0

Кстати, само определение 'sizeof' заключается в том, что он возвращает размер в кратных символах' char', поэтому этим определением 'sizeof (char)' является ровно один. Всегда. –

ответ

1

Этот обратный вызов может быть несколько раз в файл. Вы не должны создавать новый поток файлов при каждом вызове функции - вы должны передать его с использованием параметра пользовательских данных. В противном случае вы просто сохраните перезапись данных в начале файла.

Вот пример реализации:

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

Кроме того, необходимо настроить вызов curl_easy_setopt с параметром CURLOPT_WRITEDATA фактически передать свой поток файла. Убедитесь, что поток не выходит из области действия во время выполнения функций!

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; 
    } 
} 
+0

Простите меня, если я ошибаюсь, но это не создает утечку памяти? выходной файл не закрывается до возвращения функции? – silvergasp

+0

Он закрыт, когда объект std :: ofstream выходит из области действия и запускается его деструктор. –

Смежные вопросы