使用libCurl實現斷點下載
關鍵部分程式碼如下:
#include "curl.h"
#pragma comment(lib, "libcurl.lib")
size_t CROS_DownloadDlg::getContentLengthFunc(void * ptr, size_t size, size_t nmemb, void * stream)
{
LONGLONG len = 0;
int r = sscanf((const char *)ptr, "Content-Length:%I64d\n",&len);
if(r)
*((LONGLONG *) stream) = len;
return size * nmemb;
}
//根據URL獲取下載檔案大小
LONGLONG CROS_DownloadDlg::getDownloadFileSize(const char* url)
{
for(int iTry = 0 ; iTry < 3 ; iTry ++)//由於curl_easy_perform可能會有偶發性的CURLE_WRITE_ERROR錯誤,所以新增重試機制
{
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_HEADER, 1);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(handle, CURLOPT_NOBODY, 1);
if (res == CURLE_OK) {
resGetInfo = curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &lensize);
if(CURLE_OK == resGetInfo){
curl_easy_cleanup(handle);
return (LONGLONG)lensize;
}
}
curl_easy_cleanup(handle);
Sleep(200);
}
return 0;
}
size_t CROS_DownloadDlg::downLoadPackage(void *ptr, size_t size, size_t nmemb, void *userdata)
{
return 0;
return fwrite(ptr, size, nmemb, (FILE *)userdata);
}
int CROS_DownloadDlg::assetsManagerProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
if(m_bStop)
return -1;
if(totalToDownload > 0.0)
{
CROS_DownloadDlg* pDlg = (CROS_DownloadDlg*)ptr;
pDlg->m_lProcessSize = (LONGLONG)nowDownloaded;
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 1, 0);
}
return 0;
}
/*
0 設定總大小
1 設定當前下載
2 下載完成
3 使用者停止下載,其他異常終止下載
4 網路問題引發的下載異常
5 裝置斷開連線
6 磁碟剩餘空間不足
*/
UINT CROS_DownloadDlg::DownloadProc(LPVOID lpParam)
{
//////////////////////////////////////////////////////////////////////////使用CURL實現下載
string sURL = CStringConverter::Unicode2Ansi(strURL.GetString()), sSavePath = CStringConverter::Unicode2Ansi(strSavePath.GetString());
LONGLONG local_file_len = GetFileSize(strSavePath);
pDlg->m_lLocalFileCurSize = local_file_len;
curl_global_init(CURL_GLOBAL_DEFAULT);
LONGLONG remote_file_len = getDownloadFileSize(sURL.c_str());
if(!remote_file_len)
{
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 4, 0);//網路問題獲取失敗
return 0;
}
pDlg->m_lTotalSize = remote_file_len;
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 0, 0);
if(local_file_len == remote_file_len)
{
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 2, 0);
return 0;
}
//得到準確大小後應判斷strSavePath空間是否足夠先
if(remote_file_len >= (pDlg->m_ulDiskSpaceMax))
{
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 6, 0);
return 0;
}
FILE *fp = fopen(sSavePath.c_str(), "ab+");
if(!fp)
{
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 3, 0);
return 0;
}
LONGLONG filesize = 0;
m_pCurl = curl_easy_init();//curl初始化
CURLcode res;
curl_easy_setopt(m_pCurl, CURLOPT_URL, sURL.c_str());//設定curl的連結
curl_easy_setopt(m_pCurl, CURLOPT_CONNECTTIMEOUT,5);//設定連線超時,單位秒
curl_easy_setopt(m_pCurl, CURLOPT_HEADERFUNCTION,getContentLengthFunc);
curl_easy_setopt(m_pCurl, CURLOPT_HEADERDATA, &filesize);
curl_easy_setopt(m_pCurl, CURLOPT_RESUME_FROM_LARGE, local_file_len);//斷點下載設定
curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, downLoadPackage);
curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, fp);//設定將data寫入到檔案流fp中
curl_easy_setopt(m_pCurl, CURLOPT_FOLLOWLOCATION, 1L);//設定重定位URL,使用自動跳轉,返回的頭部中有Location(一般直接請求的url沒找到),則繼續請求Location對應的資料
curl_easy_setopt(m_pCurl, CURLOPT_MAXREDIRS,5);//查詢次數,防止查詢太深
curl_easy_setopt(m_pCurl, CURLOPT_NOPROGRESS, false);//設為false 下面才能設定進度響應函式
curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSFUNCTION, assetsManagerProgressFunc);//進度響應函式
curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSDATA, pDlg);//資料傳輸的物件
curl_easy_setopt(m_pCurl, CURLOPT_VERBOSE,1L);
res = curl_easy_perform(m_pCurl);
curl_easy_cleanup(m_pCurl);
curl_global_cleanup();
m_pCurl = NULL;
if (res != CURLE_OK)
{
fclose(fp);
if(res == CURLE_ABORTED_BY_CALLBACK)//點選停止下載
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 3, 0);
else if(res == CURLE_RECV_ERROR)//斷網 /* 56 - failure in receiving network data */
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 4, 0);
else
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 3, 0);
return 0;
}
fclose(fp);
pDlg->SendMessage(WM_DOWNLOADPROC_MSG, 2, 0);
return 0;
}
void CROS_DownloadDlg::OnClickBtnStop()
{
if(m_pCurl){
curl_easy_pause(m_pCurl, CURLPAUSE_RECV);
if(IDYES == ShowMessage(IDS_DEF_ROS_SURE_TO_STOP, CiPhoneUtil::GetApplicationName(), SM_ICON_QUESTION|SM_YES_AND_NO)){
m_btnStop.EnableWindow(FALSE);
m_bStop = true;
return;
}
curl_easy_pause(m_pCurl, CURLPAUSE_RECV_CONT);
}
}
////////////////////////////////////////////////
另參考連結:點選開啟連結
////////////////////////////////////////////////
另:curl設定支援https
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);//不驗證證書和HOST