参考了代码QT中使用libCurl实现多线程分块下载单个文件,封装成QT使用的类,对于进度显示更方便,默认最多使用4个线程下载单个文件。

//author:autumoon
//联系QQ:4589968
//日期:2020-11-23
#ifndef QLIBCURL_H
#define QLIBCURL_H

#include <QObject>

//回调函数序列
typedef int (*pProgressFunc)(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded);

class QLibCurl : public QObject
{
    Q_OBJECT
public:
    explicit QLibCurl(QObject *parent = nullptr);

    //使用多个线程下载单个文件
    bool DownLoad(int threadNum, std::string Url, std::string Path, std::string fileName);

public slots:

Q_SIGNALS:
    //第三个参数为线程id,取值0到3,分别代表四个下载线程的进度
    void progress(qint64, qint64, int);
    void finished(bool);

private:
    long getDownloadFileLenth (const char *url);
    static size_t writeFunc (void *ptr, size_t size, size_t nmemb, void *userdata);
    static void *workThread (void *pData);

    void sleep(int msec);

private:
    //最多使用4个线程下载单个文件,同时显示各自的进度
    static int nProgress0(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded);
    static int nProgress1(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded);
    static int nProgress2(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded);
    static int nProgress3(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded);

private:
    static bool m_busy;
    static int threadCnt;
    static pthread_mutex_t g_mutex;
    static std::vector<pProgressFunc> m_vpProgressFunc;
    static QLibCurl* m_pLibCurl;
};

#endif // QLIBCURL_H

实现文件:

//author:autumoon
//联系QQ:4589968
//日期:2020-11-23
#include "QLibCurl.h"
#include "curl/curl.h"
#include <QEventLoop>
#include <QTimer>

//全局变量,共用
bool QLibCurl::m_busy = false;
int QLibCurl::threadCnt = 0;
QLibCurl*  QLibCurl::m_pLibCurl = nullptr;
pthread_mutex_t QLibCurl::g_mutex = PTHREAD_MUTEX_INITIALIZER;
std::vector<pProgressFunc> QLibCurl::m_vpProgressFunc;

struct tNode
{
    FILE *fp;
    long startPos;
    long endPos;
    void *curl;
    pthread_t tid;
};

QLibCurl::QLibCurl(QObject *parent) : QObject(parent)
{
    m_vpProgressFunc.push_back(&QLibCurl::nProgress0);
    m_vpProgressFunc.push_back(&QLibCurl::nProgress1);
    m_vpProgressFunc.push_back(&QLibCurl::nProgress2);
    m_vpProgressFunc.push_back(&QLibCurl::nProgress3);

    m_pLibCurl = this;
}

bool QLibCurl::DownLoad(int threadNum, std::string Url, std::string Path, std::string fileName)
{
    if(m_busy)
    {
        return false;
    }
    else
    {
        m_busy = true;
    }

    long fileLength = getDownloadFileLenth (Url.c_str ());

    if (fileLength <= 0)
    {
        printf ("get the file length error...");
        return false;
    }

    // Create a file to save package.
    if (Path.length() > 1 && Path.at(Path.length() - 1) != '/')
    {
        Path += '/';
    }

    const std::string outFileName = Path + fileName;
    FILE *fp = fopen (outFileName.c_str (), "wb");
    if (!fp)
    {
        emit finished(false);
        return false;
    }

    long partSize = fileLength / threadNum;

    for (int i = 0; i <= threadNum; i++)
    {
        tNode *pNode = new tNode ();

        if (i < threadNum)
        {
            pNode->startPos = i * partSize;
            pNode->endPos = (i + 1) * partSize - 1;
        }
        else
        {
            if (fileLength % threadNum != 0)
            {
                pNode->startPos = i * partSize;
                pNode->endPos = fileLength - 1;
            }
            else
                break;
        }

        CURL *curl = curl_easy_init ();

        pNode->curl = curl;
        pNode->fp = fp;

        char range[64] = { 0 };
        snprintf (range, sizeof (range), "%ld-%ld", pNode->startPos, pNode->endPos);

        // Download pacakge
        curl_easy_setopt (curl, CURLOPT_URL, Url.c_str ());
        curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, writeFunc);
        curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) pNode);
        curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0L);

        if(m_vpProgressFunc.size() > i)
        {
            curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, m_vpProgressFunc[i]);
        }

        curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L);
        curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
        curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, 5L);
        curl_easy_setopt (curl, CURLOPT_RANGE, range);

        pthread_mutex_lock (&g_mutex);
        threadCnt++;
        pthread_mutex_unlock (&g_mutex);
        int rc = pthread_create (&pNode->tid, nullptr, workThread, pNode);
    }

    while (threadCnt > 0)
    {
        sleep(1000);
    }

    fclose (fp);

    printf ("download succed......\n");

    emit finished(true);

    m_busy = false;

    return true;
}

void QLibCurl::sleep(int msec)
{
    QEventLoop loop;//定义一个新的事件循环
    QTimer::singleShot(msec, &loop, SLOT(quit()));//创建单次定时器,槽函数为事件循环的退出函数
    loop.exec();//事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}

int QLibCurl::nProgress0(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
    Q_UNUSED(ptr)
    Q_UNUSED(nowUpLoaded)
    Q_UNUSED(totalToUpLoad)
    m_pLibCurl->emit progress(static_cast<int>(nowDownloaded), static_cast<int>(totalToDownload), 0);

    return 0;
}

int QLibCurl::nProgress1(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
    Q_UNUSED(ptr)
    Q_UNUSED(nowUpLoaded)
    Q_UNUSED(totalToUpLoad)
    m_pLibCurl->emit progress(static_cast<int>(nowDownloaded), static_cast<int>(totalToDownload), 1);

    return 0;
}

int QLibCurl::nProgress2(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
    Q_UNUSED(ptr)
    Q_UNUSED(nowUpLoaded)
    Q_UNUSED(totalToUpLoad)
    m_pLibCurl->emit progress(static_cast<int>(nowDownloaded), static_cast<int>(totalToDownload), 2);

    return 0;
}

int QLibCurl::nProgress3(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
    Q_UNUSED(ptr)
    Q_UNUSED(nowUpLoaded)
    Q_UNUSED(totalToUpLoad)
    m_pLibCurl->emit progress(static_cast<int>(nowDownloaded), static_cast<int>(totalToDownload), 3);

    return 0;
}

long QLibCurl::getDownloadFileLenth(const char *url)
{
    double downloadFileLenth = 0;
    CURL *handle = curl_easy_init ();
    curl_easy_setopt (handle, CURLOPT_URL, url);
    curl_easy_setopt (handle, CURLOPT_HEADER, 1);	//只需要header头
    curl_easy_setopt (handle, CURLOPT_NOBODY, 1);	//不需要body
    if (curl_easy_perform (handle) == CURLE_OK)
    {
        curl_easy_getinfo (handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
    }
    else
    {
        downloadFileLenth = -1;
    }
    return downloadFileLenth;
}

size_t QLibCurl::writeFunc(void *ptr, size_t size, size_t nmemb, void *userdata)
{
    tNode *node = (tNode *) userdata;
    size_t written = 0;
    pthread_mutex_lock (&g_mutex);
    if (node->startPos + size * nmemb <= node->endPos)
    {
        fseek (node->fp, node->startPos, SEEK_SET);
        written = fwrite (ptr, size, nmemb, node->fp);
        node->startPos += size * nmemb;
    }
    else
    {
        fseek (node->fp, node->startPos, SEEK_SET);
        written = fwrite (ptr, 1, node->endPos - node->startPos + 1, node->fp);
        node->startPos = node->endPos;
    }
    pthread_mutex_unlock (&g_mutex);

    return written;
}

void *QLibCurl::workThread(void *pData)
{
    tNode *pNode = (tNode *) pData;

     int res = curl_easy_perform (pNode->curl);

     if (res != 0)
     {

     }

     curl_easy_cleanup (pNode->curl);

     pthread_mutex_lock (&g_mutex);
     threadCnt--;
     printf ("thred %ld exit\n", pNode->tid);
     pthread_mutex_unlock (&g_mutex);
     delete pNode;
     pthread_exit (nullptr);
}

欢迎交流。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐