轉載: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 once class 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;//url CURL *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 server double getDownloadFileLength(string url); // Get the local file size int 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/second double 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 data ret = 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 windows if (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=0 ret = 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 server m_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;//控件ID int current;//進度 CString speed;//下載速速 CString remaintime;//下載剩余時間 }PARAMS, *PPARAMS;
typedef struct { CString *sender;//控件ID CURL *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庫支持下載過程中暫停,然后可以恢復下載,但在實際使用過程中遇到一個問題,暫停時間不長,可以正常恢復下載,如果暫停時間很長的話,再恢復時就沒法繼續下載了,這個問題還沒解決。
最后效果: