libcurl实现的异步下载图片

DownloadThread.h

#ifndef DOWNLOAD_THREAD_H
#define DOWNLOAD_THREAD_H

#include <functional>
#include <mutex>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <curl/curl.h>

typedef struct _Task_
{
    int                 nIndex;          // 图片index
    std::string         strUrl;          // url: http or https
    std::string         strDescription;   // 用于标识不同的图片(ID),透传到Callback
    std::string         strData;         // 下载得到的数据
    int                 nErrorCode;      // 错误码
    std::string         strErrorMsg;     // 错误信息
} Task;

using DownloadThreadCallback = std::function<void(const Task&)>;

class DownloadThread
{
public:
    DownloadThread() = default;
    ~DownloadThread()=default;
    int Initialize(const DownloadThreadCallback &cb);
    int AddDownloadTask(const Task &task);
    void Finish();

private:
    int InitializeCURL();
    void FinishCURL();
    void Run();
    void CallBack(const Task &t);
private:
    int                     m_threadId;
    DownloadThreadCallback  m_callback;
    CURL                   *m_curl_handle;
    std::thread            *m_thread;
    std::atomic<bool>       m_bIsRunning;
    std::list<Task>         m_taskList;
    std::mutex              m_mutexTaskList;
    std::condition_variable m_condition;
};

#endif // DOWNLOAD_THREAD_H

DownloadThread.cpp

#include "DownloadThread.h"

#include "string.h"
#include <iostream>
#include <string>

static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;

    char* memory = (char*)malloc(realsize);
    if (memory == NULL) {
        /* out of memory! */
        printf("not enough memory (realloc returned NULL)\n");
        return 0;
    }
    memcpy(memory, contents, realsize);

    Task *t = (Task*)userp;
    t->strData.append(memory, realsize);

    free(memory);

    return realsize;
}


int DownloadThread::Initialize(const DownloadThreadCallback &cb)
{
    m_callback = cb;
    if (InitializeCURL() != 0)
    {
        return -1;
    }

    m_bIsRunning = true;
    m_thread = new std::thread(&DownloadThread::Run, this);
}

void DownloadThread::Finish()
{
    m_bIsRunning = false;
    if (m_thread->joinable())
    {
        m_thread->join();
    }

    FinishCURL();
}

void DownloadThread::FinishCURL()
{
    /* cleanup curl stuff */
    curl_easy_cleanup(m_curl_handle);

    /* we're done with libcurl, so clean it up */
    curl_global_cleanup();
}

int DownloadThread::AddDownloadTask(const Task &task)
{
    std::lock_guard<std::mutex> lk(m_mutexTaskList);
    m_taskList.push_back(task);
    m_condition.notify_one();
    return 0;
}

void DownloadThread::Run()
{
    while (m_bIsRunning.load())
    {
        //std::cout << "thread is running" << std::endl;

        std::unique_lock<std::mutex>  lk(m_mutexTaskList);
        while (m_taskList.empty())
        {
            m_condition.wait(lk);
        }

        // get first element
        Task task;
        {
            //std::lock_guard<std::mutex> lkg(m_mutexTaskList);
            task = m_taskList.front();
            m_taskList.pop_front();
        }
        //std::cout << "get task url: " << task.strUrl << std::endl;
        
        CURLcode res;
        curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, (void *)&task);
        /* specify URL to get */
        curl_easy_setopt(m_curl_handle, CURLOPT_URL, const_cast<char*>(task.strUrl.c_str()));
        //curl_easy_setopt(m_curl_handle, CURLOPT_URL, "http://10.66.91.15:7777/ld/smog/2612_src.jpg");

        /* get it! */
        res = curl_easy_perform(m_curl_handle);
        /* check for errors */
        if (res != CURLE_OK) {
            // fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            task.nErrorCode = res;
            task.strErrorMsg = curl_easy_strerror(res);
        }
        else {
            /*
            * Now, our chunk.memory points to a memory block that is chunk.size
            * bytes big and contains the remote file.
            *
            * Do something nice with it!
            */
            /*printf("%lu bytes retrieved\n", task.strData.size());*/
            long lStateCode = 0;
            res = curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &lStateCode);
            if (res != CURLE_OK || lStateCode != 200)
            {
                task.nErrorCode = lStateCode;
                // task.strErrorMsg = curl_easy_strerror(res);
                task.strErrorMsg = "http state code is " + std::to_string(lStateCode);
            }
        }

        // callback
        CallBack(task);
    }
}

int DownloadThread::InitializeCURL()
{
    CURLcode error;

    curl_global_init(CURL_GLOBAL_ALL);
    /* init the curl session */
    m_curl_handle = curl_easy_init();

    ///* specify URL to get */
    ////curl_easy_setopt(curl_handle, CURLOPT_URL, "http://10.66.91.15:7777/ld/smog/2612_src.jpg");

    /* send all data to this function */
    curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    /* we pass our 'chunk' struct to the callback function */
    /*curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, (void *)this);*/

    curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1);


    /* some servers don't like requests that are made without a user-agent
    field, so we provide one */
    //curl_easy_setopt(m_curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    // https, skip the verification of the server's certificate.
    curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);

    /* 设置连接超时,单位:毫秒 */
    curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 1000L);

    // add by yexiaoyogn 10 second time out 
    curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT_MS, 2000);

    //add yexiaoyong set time out
    curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 3);
}

void DownloadThread::CallBack(const Task &t)
{
    m_callback(t);
}

demo.cpp

#include "DownloadThread.h"

#include <iostream>
#include <thread>
#include <sstream>
#include <fstream>
#include <string>

void TaskCallBack(const Task &t)
{
    std::ostringstream str;
    str << "callback ...." << std::endl;
    str << "task id is " << t.nIndex << " url is " << t.strUrl << " data size is " << t.strData.size() << std::endl;
    str << "error number is " << t.nErrorCode << " error msg is " << t.strErrorMsg << std::endl;
    std::cout << str.str() << std::endl;

    if (t.nErrorCode == 0)
    {
        std::string filename = t.strDescription + ".jpg";
        std::fstream file;
        file.open(filename, std::fstream::in | std::fstream::out | std::fstream::binary | std::fstream::app);
        file.write(t.strData.c_str(), t.strData.size());
        file.close();
    }
}


int main()
{
    DownloadThread dt;
    dt.Initialize(TaskCallBack);

    Task t;
    int cnt = 0;
    while (true)
    {
        t.nIndex = cnt++;
        t.strDescription = "task_"+std::to_string(cnt);
        t.strUrl = "http://10.66.91.15:7777/ld/smog/2612_src.jpg";
        dt.AddDownloadTask(t);

        t.nIndex = cnt++;
        t.strDescription = "task_" + std::to_string(cnt);
        t.strUrl = "http://www.baidu.com";
        dt.AddDownloadTask(t);

        t.nIndex = cnt++;
        t.strDescription = "task_" + std::to_string(cnt);
        t.strUrl = "http://10.66.91.15:7777/ld/smog/2612_srcxzz.jpg";
        dt.AddDownloadTask(t);
    }

    while (true){
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    dt.Finish();
}
posted @ 2018-09-10 17:25  yvhqbat  阅读(752)  评论(0编辑  收藏  举报