转载: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库支持下载过程中暂停,然后可以恢复下载,但在实际使用过程中遇到一个问题,暂停时间不长,可以正常恢复下载,如果暂停时间很长的话,再恢复时就没法继续下载了,这个问题还没解决。
最后效果: