使用libcurl来下载文件


/*!
* Email: scictor@gmail.com
* Auth:  scictor
* Date:  6/9/2020
* File:  DownloaderMainT.cpp
* Class: %{Cpp:License:ClassName} (if applicable)
* variable:
* Brief:
* Note:
 */

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

#pragma comment(lib,"libcurl.lib")

int _tmain(int argc, char* argv[])
{
    DWORD tick = GetTickCount();
    CDownloader murl;
     DLIO mDlWork;

    strcpy(mDlWork.url, "http://sw.bos.baidu.com/sw-search-sp/software/f69ab46476e8e/TGPSetup_2.3.2.4083.exe");
    strcpy(mDlWork.filePath, ".\\DownloadSoft\\");
    murl.AddDownloadWork(mDlWork);      //添加到下载任务中

    strcpy(mDlWork.url, "http://sw.bos.baidu.com/sw-search-sp/software/16f6d358815f2/iTunes_12.5.1.21.exe");
    strcpy(mDlWork.filePath, ".\\DownloadSoft\\");
    murl.AddDownloadWork(mDlWork);      //添加到下载任务中

    murl.StartDownloadThread();         //开启下载线程
    CURDI curInfo;
    double curDownloadLen,preLen = 0.0;
    while(1)
    {
        if(murl.IsDownloadBegin())
        {
            murl.GetCurrentDownloadInfo(&curInfo);       //获取每次下载的信息(一次相当于毫秒级,这里速度也用毫秒计算)
            curDownloadLen = curInfo.CurDownloadLen;
            printf("正在下载:%s,下载进度:%6.2lf%%,下载速度:%9.2lfKB/s\r",curInfo.fileName,
                ((double)curInfo.preLocalLen+curInfo.CurDownloadLen)/curInfo.totalFileLen*100,(curDownloadLen-preLen)/(double)(GetTickCount()-tick));
            tick = GetTickCount();
            Sleep(500);
        }
        if(murl.IsDownloadEnd()) break;
        preLen = curDownloadLen;
    }
    return 0;
}
/*!
* Email: scictor@gmail.com
* Auth:  scictor
* Date:  6/9/2020
* File:  Downloader.cpp
* Class: Downloader (if applicable)
* variable:
* Brief:
* Note:
 */

#include "Downloader.h"
//#include 

CDownloader::CDownloader(void)
{
    m_downloadCourse = -1;
    m_nConnectTimeOut = 0;
    curl_global_init (CURL_GLOBAL_ALL);
    for(int i=0; i)
    {
        memset(m_dowloadWork->url, 0, 512);
        memset(m_dowloadWork->filePath, 0, 256);
    }
    m_curIndex = 0;
}


CDownloader::~CDownloader(void)
{
    curl_global_cleanup();
}

bool CDownloader::IsDownloadBegin()
{
    if(m_downloadCourse == 0)
        return true;
    return false;
}

bool CDownloader::IsDownloadEnd()
{
    if(m_downloadCourse == 1)
        return true;
    return false;
}

bool CDownloader::CreateMultiDir(const char* pathName)
{
    if(pathName == NULL) return false;
    char filePath[256] = {0};
    strcpy(filePath, pathName);
    int i = 0, pathLen = strlen(pathName);
    CString curPath;
    char curFilePath[256] = {0};
    WIN32_FIND_DATA swf;
    if(filePath[pathLen - 1] != '\\')    //最后一个非0字符不是‘\\’则加上
    {
        filePath[pathLen] = '\\';
    }
    while(filePath[i] != '\0')
    {
        if(filePath[i] == ':')
        {
            i+=2;
            continue;
        }
        if(filePath[i] == '\\')
        {
            memcpy(curFilePath, filePath, i);
            curFilePath[i] = '\0';
            curPath = curFilePath;
            if(FindFirstFile(curPath, &swf) == INVALID_HANDLE_VALUE) //目录不存在就创建
            {
                if(!CreateDirectory(curPath, NULL))
                {
                    return false;
                }
            }
        }
        i++;
    }
    return true;
}

void CDownloader::AddDownloadWork(DLIO downloadWork)
{
    char filePath[256] = {0};
    char mUrl[512] = {0};
    strcpy(mUrl, downloadWork.url);
    strcpy(filePath, downloadWork.filePath);
    int i = strlen(filePath) -1;
    bool isPath = true;
    while(filePath[i] != '\\')
    {
        if(filePath[i] == '.' && filePath[i+1] != '\0')
        {
            isPath = false;
        }
        i--;
    }
    if(isPath)
    {
        if(!CreateMultiDir(filePath))
            return;
        char fileName[256] = {0};
        GetFileNameFormUrl(fileName,mUrl);
        if(filePath[strlen(filePath)-1] != '\\')
        {
            strcat(filePath, "\\");
        }
        strcat(filePath, fileName);
    }
    else
    {
        char realPath[256] = {0};
        for(int k=0; k)
        {
            realPath[k] = filePath[k];
        }
        realPath[i] = '\\';
        if(!CreateMultiDir(realPath))
            return;
    }
    strcpy(m_dowloadWork[m_curIndex].url, mUrl);
    strcpy(m_dowloadWork[m_curIndex].filePath, filePath);
    m_curIndex++;
}

void CDownloader::GetFileNameFormUrl(char* fileName, const char* url)
{
    int urlLen = strlen(url);
    char mUrl[512] = {0};
    char fName[256] = {0};
    strcpy(mUrl, url);
    int cutIndex = 0;
    int i = urlLen - 1, j = 0;
    while(mUrl[--i] != '/');
    i++;
    while(mUrl[i] != '\0' && mUrl[i] != '?' &&mUrl[i] != '&')
    {
        fName[j++] = mUrl[i++];
    }
    fName[j] = '\0';
    strcpy(fileName, fName);
    return ;
}

long CDownloader::GetLocalFileLenth(const char* fileName)
{
    if(m_downloadCourse == 0)        //文件已经开始下载的时候,取到的是下载前本地文件的大小;
        return m_curLocalFileLenth;
    char strTemp[256] = {0};
    strcpy(strTemp,fileName);
    FILE* fp = fopen(strTemp, "rb");
    if(fp != NULL)
    {
        m_curLocalFileLenth = filelength(fileno(fp));
        fclose(fp);
        return m_curLocalFileLenth;
    }
    return 0;
}

double CDownloader::GetTotalFileLenth(const char* url)
{
    char mUrl[512] = {0};
    strcpy(mUrl, url);
    double downloadFileLenth = 0;
    CURL* pCurl = curl_easy_init();
    curl_easy_setopt(pCurl, CURLOPT_URL, mUrl);
    curl_easy_setopt(pCurl, CURLOPT_HEADER, 1L);
    curl_easy_setopt(pCurl, CURLOPT_NOBODY, 1L);
    if(curl_easy_perform(pCurl) == CURLE_OK)
    {
        curl_easy_getinfo(pCurl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth);
    }
    else
    {
        downloadFileLenth = -1;
    }
    curl_easy_cleanup(pCurl);
    return downloadFileLenth;
}


size_t CDownloader::WriteFunc(char *str, size_t size, size_t nmemb, void *stream)
{
    return fwrite(str, size, nmemb, (FILE*)stream);
}

size_t CDownloader::ProgressFunc(
    double* pFileLen,
    double t,// 下载时总大小
    double d, // 已经下载大小
    double ultotal, // 上传是总大小
    double ulnow)   // 已经上传大小
{
    if(t == 0) return 0;
    *pFileLen = d;
    return 0;
}

int CDownloader::StartDownloadThread()
{
    if(m_downloadCourse == -1||m_downloadCourse == 1)
    {
        HANDLE downloadThread = CreateThread(NULL, 0, SingleDownloadProc, this, 0, NULL);
        CloseHandle(downloadThread);
        return 0;
    }
    return -1;
}

DWORD WINAPI CDownloader::SingleDownloadProc(LPVOID lpParameter)
{
    CDownloader* pDownload = (CDownloader*)lpParameter;
    int curDLIndex = 0;
    CURL* pCurl = curl_easy_init();
    while(curDLIndex <= pDownload->m_curIndex)
    {
        char fileName[256] = {0};
        char url[512] = {0};
        strcpy(fileName, pDownload->m_dowloadWork[curDLIndex].filePath);
        strcpy(url, pDownload->m_dowloadWork[curDLIndex].url);
        strcpy(pDownload->m_curDownloadInfo.url, url);
        strcpy(pDownload->m_curDownloadInfo.fileName, fileName);
        long localFileLen = pDownload->GetLocalFileLenth(fileName);
        pDownload->m_curLocalFileLenth = localFileLen;
        pDownload->m_curDownloadInfo.preLocalLen = pDownload->m_curLocalFileLenth;
        double totalFileLen = pDownload->m_curDownloadInfo.totalFileLen = pDownload->GetTotalFileLenth(url);
        if(localFileLen >= (long)totalFileLen)        //如果需要下载文件的大小大于等于本地文件的大小,直接下载下一个文件
        {
            curDLIndex++;
            pDownload->m_downloadCourse = -1;
            continue;
        }
        FILE* fp = fopen(fileName,"ab+");
        if(fp == NULL) //文件打开错误,进行下一个文件的下载
        {
            pDownload->m_downloadCourse = -1;
            continue;
        }
        curl_easy_setopt(pCurl, CURLOPT_URL, url);
        curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, pDownload->m_nConnectTimeOut);
        curl_easy_setopt(pCurl, CURLOPT_HEADER, 0L);
        curl_easy_setopt(pCurl, CURLOPT_NOBODY, 0L);
        curl_easy_setopt(pCurl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(pCurl, CURLOPT_RESUME_FROM, localFileLen);

        curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, WriteFunc);
        curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, fp);

        curl_easy_setopt(pCurl, CURLOPT_NOPROGRESS, 0L);
        curl_easy_setopt(pCurl, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
        curl_easy_setopt(pCurl, CURLOPT_PROGRESSDATA, &(pDownload->m_curDownloadInfo.CurDownloadLen));

        pDownload->m_downloadCourse = 0;
        if(!curl_easy_perform(pCurl))
        {
            curDLIndex++;
            pDownload->m_downloadCourse = -1;
        }
        fclose(fp);
    }
    curl_easy_cleanup(pCurl);
    pDownload->m_downloadCourse = 1;
    return 0;
}

int CDownloader::GetCurrentDownloadInfo(CURDI* lpCurDownloadInfor)
{
    *lpCurDownloadInfor = m_curDownloadInfo;
    return 0;
}

int CDownloader::SetConnectTimeOut(DWORD nConnectTimeOut)
{
    if(m_downloadCourse == 0) return -1;
    else
        m_nConnectTimeOut = nConnectTimeOut;
    return 0;
}
/*!
* Email: scictor@gmail.com
* Auth:  scictor
* Date:  6/9/2020
* File:  Downloader.h
* Class: Downloader (if applicable)
* variable:
* Brief:
* Note:
 */



#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include 
#include <string.h>
#include 
//#include 

#define MAXWORK 200
typedef struct DownloadInfo
{
    char url[512];
    char filePath[256];
}DLIO;

typedef struct CurDownloadInfor
{
    char url[512];     //url
    char fileName[256];   //文件名称
    long preLocalLen;     //本地已下载的长度(大小)
    double totalFileLen;   //文件总长度(大小)
    double CurDownloadLen;   //每次下载的文件长度(大小)
}CURDI;

class CDownloader
{
public:
    CDownloader(void);
    ~CDownloader(void);
    int StartDownloadThread();
    double GetTotalFileLenth(const char* url);             //获取将要下载的文件长度
    long GetLocalFileLenth(const char* fileName);         //获取本地问价长度
    void GetFileNameFormUrl(char* fileName, const char* url);      //从URL中获取文件名
    void AddDownloadWork(DLIO downloadWork);
    int SetConnectTimeOut(DWORD nConnectTimeOut);    //设置连接的超时时间
    int GetCurrentDownloadInfo(CURDI* lpCurDownloadInfor);
    bool CreateMultiDir(const char* pathName);       //是否在本地创建目录,没有就创建
    bool IsDownloadBegin();
    bool IsDownloadEnd();
protected:
    static DWORD SingleDownloadProc(LPVOID lpParameter);       //线程函数
    static size_t WriteFunc(char *str, size_t size, size_t nmemb, void *stream);     //写入数据(回调函数)
    static size_t ProgressFunc(double* fileLen, double t, double d, double ultotal, double ulnow);   //下载进度
private:
    char m_filePath[512];
    char m_downloadUrl[256];
    int m_downloadCourse;   //-1 还未下载 0正在下载 1下载完成
    long m_curLocalFileLenth; //因为下载的时候已经计算了本地文件的大小用来设置断点,所以对于每个文件,该数字只会被设置一次;就是下载前的本地大小;
    long m_nConnectTimeOut;      //连接的超时时间
    DLIO m_dowloadWork[MAXWORK];
    CURDI m_curDownloadInfo;
    int m_curIndex;
    CURL* m_pCurl;
};


//https://blog.csdn.net/qq_25867649/java/article/details/52789467

#endif // DOWNLOADER_H