使用libcurl开源库和Duilib做的下载文件并显示进度条的小工具
轉載:http://blog.csdn.net/mfcing/article/details/43603525
轉載:http://blog.csdn.net/infoworld/article/details/46646933
轉載:http://blog.csdn.net/qq_25867649/article/details/52789467?locationNum=2
轉載:http://www.cnblogs.com/wing-h/p/3263488.html
轉載:http://blog.csdn.net/infoworld/article/details/46646933
轉載:http://blog.csdn.net/xiaojun111111/article/details/53032126
轉載:http://ysir.me/2015/08/05/libcurl%E5%AE%9E%E7%8E%B0%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0/
轉載:http://blog.csdn.net/qq_25867649/article/details/52913501
界面借用了網友mfcing,然后進行了修改
每個下載任務包含的功能:
1.任務名字
2.下載剩余時間
3.下載速度
4.下載進度百分比
5.繼續下載
6.暫停下載
7.取消下載任務
?
一、首先擴展Duilib下載List
.h文件
#pragma onceclass CDownloadListUI : public CTileLayoutUI { public:enum { SCROLL_TIMERID = 10 };CDownloadListUI(void);~CDownloadListUI(void);virtual void DoEvent(TEventUI& event);void AddItem();/*
@param strUrl 下載文件url地址
@param strFileNmae 任務文件名
*/
void AddItem(CDuiString strUrl,CDuiString strFilename); private:UINT m_uButtonState;POINT m_ptLastMouse;LONG m_dwDelayDeltaY;DWORD m_dwDelayNum;DWORD m_dwDelayLeft; };inline double CalculateDelay(double state) {return pow(state, 2); }
.cpp文件
#include "StdAfx.h" #include "DownloadListUI.h"CDownloadListUI::CDownloadListUI(void):m_uButtonState(0), m_dwDelayDeltaY(0), m_dwDelayNum(0), m_dwDelayLeft(0) { }CDownloadListUI::~CDownloadListUI(void) { }void CDownloadListUI::DoEvent( TEventUI& event ) {if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {if( m_pParent != NULL ) m_pParent->DoEvent(event);else CTileLayoutUI::DoEvent(event);return;}if( event.Type == UIEVENT_TIMER && event.wParam == SCROLL_TIMERID ){if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {POINT pt = m_pManager->GetMousePos();LONG cy = (pt.y - m_ptLastMouse.y);m_ptLastMouse = pt;SIZE sz = GetScrollPos();sz.cy -= cy;SetScrollPos(sz);return;}else if( m_dwDelayLeft > 0 ) {--m_dwDelayLeft;SIZE sz = GetScrollPos();LONG lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY);if( (lDeltaY > 0 && sz.cy != 0) || (lDeltaY < 0 && sz.cy != GetScrollRange().cy ) ) {sz.cy -= lDeltaY;SetScrollPos(sz);return;}}m_dwDelayDeltaY = 0;m_dwDelayNum = 0;m_dwDelayLeft = 0;m_pManager->KillTimer(this, SCROLL_TIMERID);return;}if( event.Type == UIEVENT_MOUSEWHEEL ){LONG lDeltaY = 0;if( m_dwDelayNum > 0 ) lDeltaY = (LONG)(CalculateDelay((double)m_dwDelayLeft / m_dwDelayNum) * m_dwDelayDeltaY);switch( LOWORD(event.wParam) ) {case SB_LINEUP:if( m_dwDelayDeltaY >= 0 ) m_dwDelayDeltaY = lDeltaY + 8;else m_dwDelayDeltaY = lDeltaY + 12;break;case SB_LINEDOWN:if( m_dwDelayDeltaY <= 0 ) m_dwDelayDeltaY = lDeltaY - 8;else m_dwDelayDeltaY = lDeltaY - 12;break;}if( m_dwDelayDeltaY > 100 ) m_dwDelayDeltaY = 100;else if( m_dwDelayDeltaY < -100 ) m_dwDelayDeltaY = -100;m_dwDelayNum = (DWORD)sqrt((double)abs(m_dwDelayDeltaY)) * 5;m_dwDelayLeft = m_dwDelayNum;m_pManager->SetTimer(this, SCROLL_TIMERID, 50U);return;}CTileLayoutUI::DoEvent(event); }void CDownloadListUI::AddItem() {CDialogBuilder builder;CContainerUI* pItem=static_cast<CContainerUI*>(builder.Create(L"Item_load.xml", 0));if ( pItem ){int i=GetCount();CDuiString strText;strText.Format(L"%02d", i+1);CControlUI* pControl=pItem->GetItemAt(0);if ( pControl )pControl->SetText(strText);pControl=pItem->GetItemAt(2);CProgressUI* pProgress=(CProgressUI*)pControl->GetInterface(L"Progress");if ( pProgress )pProgress->SetValue(i+1);pControl=pItem->GetItemAt(3);if ( pControl ){strText.Format(L"%02d:%02d:%02d", 1,1,1);pControl->SetText(strText);}pControl=pItem->GetItemAt(4);if ( pControl ){strText.Format(L"%dM/s", i+1);pControl->SetText(strText);}pControl=pItem->GetItemAt(5);if ( pControl ){strText.Format(L"%d%%", i+1);pControl->SetText(strText);}pControl=pItem->GetItemAt(6);if ( pControl )pControl->SetText(L"正在下載");pControl=pItem->GetItemAt(7);if ( pControl ){strText.Format(L"BtnLoad1%d", i);pControl->SetName(strText);pControl->SetTag(i);}pControl=pItem->GetItemAt(8);if ( pControl ){strText.Format(L"BtnLoad2%d", i);pControl->SetName(strText);pControl->SetTag(i);}pControl=pItem->GetItemAt(9);if ( pControl ){strText.Format(L"BtnLoad3%d", i);pControl->SetName(strText);pControl->SetTag(i);}Add(pItem);} }void CDownloadListUI::AddItem( CDuiString strUrl ,CDuiString strFilename) {CDialogBuilder builder;CContainerUI* pItem=static_cast<CContainerUI*>(builder.Create(L"Item_load.xml", 0));pItem->SetName(strUrl);if ( pItem ){int i=GetCount();CDuiString strText;strText.Format(L"%02d", i+1);CControlUI* pControl=pItem->GetItemAt(0);if ( pControl )pControl->SetText(strText);pControl=pItem->GetItemAt(1);if (pControl){pControl->SetText(strFilename);pControl->SetToolTip(strFilename);}pControl=pItem->GetItemAt(2);CProgressUI* pProgress=(CProgressUI*)pControl->GetInterface(L"Progress");if ( pProgress )pProgress->SetValue(1);pControl=pItem->GetItemAt(5);if ( pControl ){strText.Format(L"%d%%", 1);pControl->SetText(strText);}pControl=pItem->GetItemAt(6);if ( pControl )pControl->SetText(L"正在下載");pControl=pItem->GetItemAt(7);if ( pControl ){strText.Format(L"BtnLoad1%d", i);pControl->SetName(strText);pControl->SetTag(i);}pControl=pItem->GetItemAt(8);if ( pControl ){strText.Format(L"BtnLoad2%d", i);pControl->SetName(strText);pControl->SetTag(i);}pControl=pItem->GetItemAt(9);if ( pControl ){strText.Format(L"BtnLoad3%d", i);pControl->SetName(strText);pControl->SetTag(i);}Add(pItem);} }?把任務項寫到Item_load.xml中
<?xml version="1.0" encoding="UTF-8"?> <Window><Container width="430" height="40" inset="2,2,2,2"><!--Container--><Label float="true" pos="4,10,30,30" align="left" font="1" textcolor="#FFF9F9F9" /><Label text="軟件名稱" float="true" pos="34,10,110,30" endellipsis="true" textcolor="#FFF9F9F9" align="left" font="0" /><Progress float="true" pos="110,18,230,22" bkimage="pro1.jpg" foreimage="pro2.jpg" min="0" max="100" value="0"/> <Label text="剩余時間" float="true" pos="110,24,160,54" textcolor="#FFF9F9F9" align="left" font="0" /><Label text="下載速度" float="true" pos="165,24,230,54" textcolor="#FFF9F9F9" align="left" font="0" /><Label text="下載進度" float="true" pos="235,14,270,54" textcolor="#FFF9F9F9" align="left" font="0" /><Label float="true" pos="280,14,350,54" textcolor="#FFF9F9F9" align="left" font="0" /><Button tooltip="取消" float="true" pos="370,16,383,28" normalimage="file='buttons.png' source='28,0,41,12'" hotimage="file='buttons.png' source='28,12,41,24'" pushedimage="file='buttons.png' source='28,24,41,36'" /><Button tooltip="暫停" float="true" pos="340,16,353,28" normalimage="file='buttons.png' source='14,0,27,12'" hotimage="file='buttons.png' source='14,12,27,24'" pushedimage="file='buttons.png' source='14,24,27,36'" /><Button tooltip="繼續" visible="false" float="true" pos="340,16,353,28" normalimage="file='buttons.png' source='0,0,13,12'" hotimage="file='buttons.png' source='0,12,13,24'" pushedimage="file='buttons.png' source='0,24,13,36'" /></Container> </Window>?
二、把工作線程封裝成一個類
.h文件
#ifndef _DOWNLOADFILETHREAD_H #define _DOWNLOADFILETHREAD_H #include <windows.h> #include <process.h> typedef struct {CString url;int current;CString speed;CString remaintime; }PARAMS, *PPARAMS;typedef struct {CString *sender;//urlCURL *handle;double* downloadFileLength;int* resumeByte; }Progress_User_Data;class CDownloadFileThread { public:CDownloadFileThread(CString url,string filename);~CDownloadFileThread();void InitTask();/** 開始運行線程 **/ void Start(); void Run(); //暫停下載void PauseTask();//恢復下載void ResumeTask();//退出下載void ExitDownload();std::string UnicodeToANSI(const wstring& wstr);static size_t my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream);static int my_progress_func(void *progress_data,double t, /* dltotal */double d, /* dlnow */double ultotal,double ulnow);static unsigned int WINAPI ThreadFunction(LPVOID pParam); static size_t nousecb(char *buffer, size_t x, size_t y, void *userdata);// Get the file size on the serverdouble getDownloadFileLength(string url);// Get the local file sizeint getLocalFileLength(string filepath);CString GetCurrentUrl();private: HANDLE m_handle;HANDLE m_StartEvent;HANDLE m_EndEvent;unsigned int m_ThreadID;/*volatile*/ bool m_bIsCancel;//取消下載 CURL* m_curl;//libcurl句柄 FILE* m_outfile;//文件指針 CString m_url;//下載地址string m_filename;//文件名double m_downloadFileLength;//服務器文件長度int m_resumeByte;//本地已下載的文件長度 }; #endif//_DOWNLOADFILETHREAD_H?
.cpp文件
#include "stdafx.h" #include "DownloadFileThread.h"CDownloadFileThread::CDownloadFileThread(CString url,string filename) :m_url(url) ,m_curl(NULL) ,m_outfile(NULL) ,m_bIsCancel(false) ,m_filename(filename) ,m_downloadFileLength(-1) ,m_resumeByte(-1) {m_handle = (HANDLE)_beginthreadex(NULL,0,ThreadFunction,(void*)this,0,&m_ThreadID);m_StartEvent = CreateEvent(NULL,FALSE,FALSE,NULL);m_EndEvent = CreateEvent(NULL,FALSE,FALSE,NULL);curl_easy_init(); }CDownloadFileThread::~CDownloadFileThread() {SetEvent(m_EndEvent);if (m_handle){CloseHandle(m_handle);}if (m_StartEvent){CloseHandle(m_StartEvent);}if (m_EndEvent){CloseHandle(m_EndEvent);} }void CDownloadFileThread::Start() {SetEvent(m_StartEvent); }size_t CDownloadFileThread::my_write_func(void *ptr, size_t size, size_t nmemb, FILE *stream) {size_t nWrite =fwrite(ptr, size, nmemb, stream);if (nWrite == 0){return CURL_WRITEFUNC_PAUSE;}else{return nWrite;}//return fwrite(ptr, size, nmemb, stream); } int CDownloadFileThread::my_progress_func(void *progress_data,double t, /* dltotal */double d, /* dlnow */double ultotal,double ulnow) {//printf("%s %g / %g (%g %%)\n", progress_data, d, t, d*100.0/t); Progress_User_Data *data = static_cast<Progress_User_Data *>(progress_data);char text[256] = {0};//sprintf_s(text,sizeof(text),"%d",d*100.0/t);char timeFormat[256]= {0};CURL *easy_handle = data->handle;// Defaults to bytes/seconddouble speed;string unit = "B";int hours,minutes,seconds;curl_easy_getinfo(easy_handle, CURLINFO_SPEED_DOWNLOAD, &speed); // curl_get_info必須在curl_easy_perform之后調用if (speed != 0){double leftTime = ((*data->downloadFileLength) - d - (*data->resumeByte)) / speed;hours = leftTime / 3600;minutes = (leftTime - hours * 3600) / 60;seconds = leftTime - hours * 3600 - minutes * 60;}sprintf_s(timeFormat, sizeof(timeFormat), "%02d:%02d:%02d", hours, minutes, seconds);if (speed > 1024 * 1024 * 1024){unit = "G";speed /= 1024 * 1024 * 1024;}else if (speed > 1024 * 1024){unit = "MB";speed /= 1024 * 1024;}else if (speed > 1024){unit = "KB";speed /= 1024;}sprintf_s(text,sizeof(text),"%.2f%s/s",speed, unit.c_str());CString* url = (CString*)(data->sender);PARAMS params;params.url = url->GetBuffer();params.current = d*100.0/t;params.speed = text;params.remaintime = timeFormat;HWND hWnd = FindWindow( NULL , L"下載管理器" );if (hWnd){::SendMessage(hWnd,WM_UPDATEPROGRESS,0,(LPARAM)¶ms);}return 0; }std::string CDownloadFileThread::UnicodeToANSI( const wstring& wstr ) {int unicodeLen = ::WideCharToMultiByte(CP_ACP,0,wstr.c_str(),-1,NULL,0, NULL ,NULL);if(unicodeLen == 0) return std::string("");char *pChar= new char[unicodeLen+1];memset(pChar , 0 , sizeof( char ) * (unicodeLen+1));::WideCharToMultiByte(CP_ACP,0,wstr.c_str(),-1,pChar,unicodeLen, NULL ,NULL);pChar[unicodeLen]=0;string str = pChar;delete [] pChar;pChar=NULL;return str; }size_t CDownloadFileThread::nousecb(char *buffer, size_t x, size_t y, void *userdata) {(void)buffer;(void)userdata;return x * y; }void CDownloadFileThread::InitTask() {m_curl = curl_easy_init(); }void CDownloadFileThread::PauseTask() {m_bIsCancel = true;curl_easy_pause(m_curl,CURLPAUSE_RECV); }void CDownloadFileThread::ResumeTask() {m_bIsCancel = false;if (m_curl){curl_easy_pause(m_curl,CURLPAUSE_RECV_CONT);} }void CDownloadFileThread::ExitDownload() {curl_easy_pause(m_curl,CURLPAUSE_RECV);}CString CDownloadFileThread::GetCurrentUrl() {return m_url; }double CDownloadFileThread::getDownloadFileLength( string url ) {CURL *easy_handle = NULL;int ret = CURLE_OK;double size = -1;do{easy_handle = curl_easy_init();if (!easy_handle){break;}// Only get the header dataret = curl_easy_setopt(easy_handle, CURLOPT_URL, url.c_str());ret |= curl_easy_setopt(easy_handle, CURLOPT_HEADER, 1L);ret |= curl_easy_setopt(easy_handle, CURLOPT_NOBODY, 1L);ret |= curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, nousecb); // libcurl_a.lib will return error code 23 without this sentence on windowsif (ret != CURLE_OK){break;}ret = curl_easy_perform(easy_handle);if (ret != CURLE_OK){char s[100] = {0};sprintf_s(s, sizeof(s), "error:%d:%s", ret, curl_easy_strerror(static_cast<CURLcode>(ret)));break;}// size = -1 if no Content-Length return or Content-Length=0ret = curl_easy_getinfo(easy_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);if (ret != CURLE_OK){break;}} while (0);curl_easy_cleanup(easy_handle);return size; }//C語言 下獲取文件長度 int CDownloadFileThread::getLocalFileLength(string filepath) {FILE *fp=fopen(filepath.c_str(),"r"); if(!fp) return -1; fseek(fp,0L,SEEK_END); int size=ftell(fp); fclose(fp); return size; }
void CDownloadFileThread::Run() {CURLcode res;char *progress_data = "* ";m_curl = curl_easy_init();if(m_curl){m_resumeByte = getLocalFileLength(m_filename);// Get the file size on the serverm_downloadFileLength = getDownloadFileLength(UnicodeToANSI(m_url.GetBuffer()));if(m_resumeByte >= (int)m_downloadFileLength)return;m_outfile = fopen(m_filename.c_str(), "ab+");Progress_User_Data data = { &m_url, m_curl,&m_downloadFileLength,&m_resumeByte};curl_easy_setopt(m_curl, CURLOPT_URL, UnicodeToANSI(m_url.GetBuffer()).c_str());//在此要注意,此url必須是多字節curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 0);curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, m_outfile);curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, my_write_func);curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, FALSE);curl_easy_setopt(m_curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);curl_easy_setopt(m_curl, CURLOPT_PROGRESSDATA, &data);//給下載進度回調函數傳參數,在此是指針res = curl_easy_perform(m_curl);fclose(m_outfile);/* always cleanup */curl_easy_cleanup(m_curl);}}unsigned int WINAPI CDownloadFileThread::ThreadFunction( LPVOID pParam ) {CDownloadFileThread* pthis = (CDownloadFileThread*)pParam;HANDLE hThrds[2];hThrds[0] = pthis->m_StartEvent;hThrds[1] = pthis->m_EndEvent;while(true){DWORD result = WaitForMultipleObjects(2,hThrds,FALSE, INFINITE);if (result == WAIT_OBJECT_0){pthis->Run();}else if (result == WAIT_OBJECT_0+1){break;}}return 0; }
?
因為下載任務比較耗時,所以在工作線程中進行下載,然后在UI線程顯示進度,在此我用的是工作線程發消息給UI線程,然后進行進度顯示。
1.這里要給界面上任務Item設置一個控件ID,這樣更新進度,可以知道具體更新哪個任務的進度
2.界面添加任務時,創建一個下載對象,把url傳給下載對象。
3.工作線程拿到下載進度后,用SendMessage發給UI線程,發送時要把下載用的url傳回去(其實也可以用索引作為作為ID)
?
typedef struct {CString url;//控件IDint current;//進度CString speed;//下載速速CString remaintime;//下載剩余時間 }PARAMS, *PPARAMS;?
typedef struct {CString *sender;//控件IDCURL *handle;libcurl指針double* downloadFileLength;//服務上文件長度int* resumeByte;//本地已下載文件長度 }Progress_User_Data;通過url拿到文件名,這個不是所有的都可以拿到,因為有的url中沒有名字
void 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 ; }?
更新進度的消息響應函數
LRESULT CMainWnd::OnRefresh(UINT uMsg, WPARAM wParam, LPARAM lParam,BOOL& bHandled) {PARAMS* pParam = (PARAMS*)lParam;CDuiString strText;for (int i = 0; i < m_pDownloadList->GetCount();i++){CControlUI* pControl = m_pDownloadList->GetItemAt(i);if ( _tcscmp(pParam->url.GetBuffer(), pControl->GetName())==0 ){CContainerUI* pContain = static_cast<CContainerUI*>(pControl);CControlUI* pControl=pContain->GetItemAt(2);CProgressUI* pProgress=(CProgressUI*)pControl->GetInterface(L"Progress");if ( pProgress )pProgress->SetValue(pParam->current);pControl=pContain->GetItemAt(3);if ( pControl ){pControl->SetText(pParam->remaintime);}pControl=pContain->GetItemAt(4);if ( pControl ){pControl->SetText(pParam->speed.GetBuffer());}pControl=pContain->GetItemAt(5);if ( pControl ){strText.Format(L"%d%%", pParam->current);pControl->SetText(strText);}break;}}return 0; }?
三、libcurl庫支持下載過程中暫停,然后可以恢復下載,但在實際使用過程中遇到一個問題,暫停時間不長,可以正常恢復下載,如果暫停時間很長的話,再恢復時就沒法繼續下載了,這個問題還沒解決。
?
最后效果:
?
?
下載源碼Demo
總結
以上是生活随笔為你收集整理的使用libcurl开源库和Duilib做的下载文件并显示进度条的小工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue2.0 Transition常见用
- 下一篇: Codeforces 38B - Che