一個基於WinHttp的輕量級的分片下載庫介紹
作者:magictong
日期:2018/04/09
資源下載
https://download.csdn.net/download/magictong/10370195
主要目的
1、下載檔案到記憶體。
2、下載分片Range檔案(譬如下載某個資原始檔的第100位元組到第150位元組的內容)。
3、不需要回調執行緒是UI執行緒(有訊息迴圈)。
適用場景
1、小檔案全量下載(注:暫未支援斷點續傳)。
2、分片檔案下載(注:下載某個檔案中的一段內容)。
3、支援https協議(僅支援只需要驗證伺服器端的情況)。
適用系統
受限於WinHttp的支援,本庫需要Windows XP SP1以上或者Windows 2000 SP3以上才能使用。
標頭檔案介紹
1、QMTINYDL::IQMTinyDLSink介面
使用類需要從QMTINYDL::IQMTinyDLSink介面進行繼承,實現它的三個虛擬函式(OnTinyDLComplete,OnTinyDLProgress,OnTinyDLError),OnTinyDLComplete必須實現,其它兩個可以按需要實現,這個介面主要用於下載完成,下載失敗,下載出錯,下載進度的回撥,使用者只有通過此介面的回撥感知下載庫的工作進度。回撥函式引數說明參考下面的註釋。
//-------------------------------------------------------------------------
// 類名 : IQMTinyDLSink
// 功能 : QMTinyDL
// 附註 :使用IQMTinyDLMgr介面進行下載的類需要繼承本介面
// -------------------------------------------------------------------------
class IQMTinyDLSink
{
public:
virtual ~IQMTinyDLSink() {}
//-------------------------------------------------------------------------
// 函式 :OnTinyDLComplete
// 功能 :下載結束回撥(
// 返回值 : virtualvoid
// 引數 : LONGlTaskID任務id
// 引數 : void*pBuffer如果是下載到記憶體需求,則指向該記憶體,如果是下載到檔案,則指向(wchar*)檔案路徑
// 引數 : DWORDdwSize下載總長度
// 引數 : DWORDdwErr錯誤碼
// 引數 : PVOIDpContext上下文引數
// 附註 :必須實現
//-------------------------------------------------------------------------
virtual void OnTinyDLComplete(LONG lTaskID, void* pBuffer, DWORD dwSize,DWORD dwErr, PVOID pContext) = 0;
//-------------------------------------------------------------------------
// 函式 :OnTinyDLProgress
// 功能 :下載進度回撥
// 返回值 : virtualvoid
// 引數 : LONGlTaskID任務id
// 引數 : DWORDdwLen當前下載長度
// 引數 : DWORDdwTotalLen需要下載總長度
// 引數 : PVOIDpContext上下文引數
// 附註 :可選實現
//-------------------------------------------------------------------------
virtual void OnTinyDLProgress(LONG lTaskID, DWORD dwLen, DWORDdwTotalLen, PVOID pContext) {}
//-------------------------------------------------------------------------
// 函式 :OnTinyDLError
// 功能 :下載中途錯誤回撥
// 返回值 : virtualvoid
// 引數 : LONGlTaskID任務id
// 引數 : DWORDdwErr錯誤碼
// 引數 : PVOIDpContext上下文引數
// 附註 :可選實現
//-------------------------------------------------------------------------
virtual void OnTinyDLError(LONG lTaskID, DWORD dwErr, PVOID pContext) {}
};
//-------------------------------------------------------------------------
// 列舉名 : ERRORCODE
// 功能 :下載失敗錯誤碼
// 附註 :通過DWORD dwErr引數傳遞
//-------------------------------------------------------------------------
enum
{
ERRORCODE_NULL=0, //SUCCESS
ERRORCODE_NOMODIFIED = 1, // Nomodified . so no update
ERRORCODE_NETWORKFAILED = 2,
ERRORCODE_OPEN_HTTP = 3,
ERRORCODE_FILE_ERROR = 4,
ERRORCODE_USER_ABORT = 5,
ERRORCODE_FAILED = 6,
//////////////////////////////////////////////////////////////////////////
// 建立下載例項失敗,申請buffer失敗等
// add by magictong 2016/12/16 11:33:33
//
ERRORCODE_CREATE_HTTPDOWNLOAD_FAILED = 7,
ERRORCODE_NEWBUF_FAILED = 8,
ERRORCODE_STOP_HANDLE_NULL = 9,
ERRORCODE_NOT_SUPPORT_WINHTTP = 10,
};
2、QMTINYDL:: IQMTinyDLMgr
使用類進行下載請求前,需要先獲得QMTINYDL:: IQMTinyDLMgr介面,該介面由4個啟動方法,1個反註冊介面,2個停止方法和1個代理設定介面組成。詳細引數資訊參考下面的對應註釋。
//-------------------------------------------------------------------------
// 類名 : IQMTinyDLMgr
// 功能 : QMTinyDL外部使用介面
// 附註 :
// -------------------------------------------------------------------------
class IQMTinyDLMgr
{
public:
~IQMTinyDLMgr() {}
//-------------------------------------------------------------------------
// 函式 :CHTTPDownloadEx::StartTaskToBuf
// 功能 :下載到記憶體,需要設定大小
// 返回值 : DWORD任務id(當前程序唯一)
// 引數 :IQMTinyDLSink* pCallbackObj回撥介面
// 引數 :LPCWSTR pUrl下載連結
// 引數 :DWORD64 dw64Size下載大小
// 引數 : PVOIDpContext上下文引數
// 附註 :注意:需要設定大小,內部需要提前分配記憶體
//-------------------------------------------------------------------------
virtual DWORD StartTaskToBuf(
IQMTinyDLSink* pCallbackObj,
LPCWSTR pUrl,
DWORD64 dw64Size,
PVOID pContext = NULL) = 0;
//-------------------------------------------------------------------------
// 函式 :CHTTPDownloadEx::StartRangeTaskToBuf
// 功能 :下載到記憶體,分片下載
// 返回值 : DWORD任務id(當前程序唯一)
// 引數 :IQMTinyDLSink* pCallbackObj回撥介面
// 引數 :LPCWSTR pUrl下載連結
// 引數 :DWORD64 dw64Offset下載偏移(0開始)
// 引數 :DWORD64 dw64Size下載分片大小
// 引數 : PVOIDpContext上下文引數
// 附註 :如果啟動失敗,則返回0,否則返回任務ID
//-------------------------------------------------------------------------
virtual DWORD StartRangeTaskToBuf(
IQMTinyDLSink* pCallbackObj,
LPCWSTR pUrl,
DWORD64 dw64Offset,
DWORD64 dw64Size,
PVOID pContext = NULL) = 0;
//-------------------------------------------------------------------------
// 函式 :CHTTPDownloadEx::StartTaskRange
// 功能 :進行下載,下載到檔案
// 返回值 : DWORD任務id(當前程序唯一)
// 引數 :IQMTinyDLSink* pCallbackObj回撥介面
// 引數 :LPCWSTR pUrl下載連結
// 引數 :LPCWSTR lpFilePath下載到目標檔案全路徑
// 引數 : PVOIDpContext上下文引數
// 附註 :如果啟動失敗,則返回0,否則返回任務ID
//-------------------------------------------------------------------------
virtual DWORD StartTask(
IQMTinyDLSink* pCallbackObj,
LPCWSTR pUrl,
LPCWSTR lpFilePath,
PVOID pContext = NULL) = 0;
//-------------------------------------------------------------------------
// 函式 :CHTTPDownloadEx::StartRangeTask
// 功能 :進行分片下載
// 返回值 : DWORD任務id(當前程序唯一)
// 引數 :IQMTinyDLSink* pCallbackObj回撥介面
// 引數 :LPCWSTR pUrl下載連結
// 引數 :LPCWSTR lpFilePath下載到目標檔案全路徑
// 引數 :DWORD64 dw64Offset下載偏移(0開始)
// 引數 :DWORD64 dw64Size下載分片大小
// 引數 : PVOIDpContext上下文引數
// 附註 :如果啟動失敗,則返回0,否則返回任務ID
//-------------------------------------------------------------------------
virtual DWORD StartRangeTask(
IQMTinyDLSink* pCallbackObj,
LPCWSTR pUrl,
LPCWSTR lpFilePath,
DWORD64 dw64Offset,
DWORD64 dw64Size,
PVOID pContext = NULL) = 0;
//-------------------------------------------------------------------------
// 函式 :StopTask
// 功能 :停止id為lTaskID的下載任務
// 返回值 : void
// 引數 : LONGlTaskID
// 附註 :
// -------------------------------------------------------------------------
virtual void StopTask(LONG lTaskID) = 0;
//-------------------------------------------------------------------------
// 函式 :StopAllTask
// 功能 :停掉所有的下載任務
// 返回值 : void
// 附註 :
//-------------------------------------------------------------------------
virtual void StopAllTask() = 0;
//-------------------------------------------------------------------------
// 函式 :SetProxy
// 功能 :設定代理資訊
// 返回值 : virtualvoid
// 引數 :QMTINYDL::QMDLPROXYTYPE proxyType,
// 引數 : PCWSTRlpszAddress
// 引數 : USHORTusPort
// 引數 :LPCWSTR lpszUserName = NULL
// 引數 :LPCWSTR lpszPassword = NULL
// 引數 :LPCWSTR lpszDomain = NULL
// 附註 :如果不呼叫SetProxy,元件內部會自動處理管家代理
//-------------------------------------------------------------------------
virtual void SetProxy(
QMTINYDL::QMDLPROXYTYPE proxyType,
LPCWSTR lpszAddress,
USHORT usPort,
LPCWSTR lpszUserName = NULL,
LPCWSTR lpszPassword = NULL,
LPCWSTR lpszDomain = NULL) = 0;
//-------------------------------------------------------------------------
// 函式 :UnregisterCallback
// 功能 :反註冊回撥
// 返回值 : virtualBOOL
// 引數 :IQMTinyDLSink* pCallbackObj
// 附註 :
//-------------------------------------------------------------------------
virtual BOOL UnregisterCallback(IQMTinyDLSink* pCallbackObj) = 0;
};
3、標頭檔案裡面QMDLProxyInfo和QMDLPROXYTYPE定義是代理相關內容,按需使用即可,另外庫內部初始化時會自動處理IE設定的代理,如果外部呼叫SetProxy方法手動設定代理,則使用手動設定的代理。
使用方法
1、實現QMTINYDL::IQMTinyDLSink介面,相關呼叫者自己按需實現即可。
2、獲得QMTINYDL:: IQMTinyDLMgr介面,獲取該介面有兩個方法:
(1) 一種是直接使用QMTinyDL.dll(MD編譯)的匯出函式獲取:
//////////////////////////////////////////////////////////////////////////
// Export原型如下
//
extern "C" IQMTinyDLMgr*__stdcall CreateQMTinyDLMgr();
extern "C" int __stdcallDestroyQMTinyDLMgr(IQMTinyDLMgr* p);
extern "C" unsigned int__stdcall GetQMTinyDLVer();
先使用CreateQMTinyDLMgr獲得IQMTinyDLMgr介面,使用完之後呼叫DestroyQMTinyDLMgr釋放。
(2) 另外一種方法是使用QMTINYDL庫的輔助lib(QMTinyDLLib.lib:MD編譯)來獲取,節省尋找載入QMTinyDL.dll的操作。
使用該lib只需要引入IQMTinyDL.h標頭檔案,使用裡面的兩個函式CreateQMTinyDLMgr和DestroyQMTinyDLMgr即可。
//-------------------------------------------------------------------------
// 函式 : CreateQMTinyDLMgr
// 功能 :建立IQMTinyDLMgr介面
// 返回值 : IQMTinyDLMgr*
// 附註 :
//-------------------------------------------------------------------------
extern "C" IQMTinyDLMgr*__stdcall CreateQMTinyDLMgr();
// -------------------------------------------------------------------------
// 函式 : DestroyQMTinyDLMgr
// 功能 :釋放IQMTinyDLMgr介面
// 返回值 : int
// 引數 : IQMTinyDLMgr* p
// 附註 :
//-------------------------------------------------------------------------
extern "C" int __stdcall DestroyQMTinyDLMgr(IQMTinyDLMgr*p);
注意:在使用QMTINYDL:: IQMTinyDLMgr介面的Start*系列方法(譬如:StartRangeTask)時,傳入了一個QMTINYDL::IQMTinyDLSink介面,在呼叫DestroyQMTinyDLMgr之前需要把該介面從庫裡面反註冊,務必呼叫QMTINYDL:: IQMTinyDLMgr介面的UnregisterCallback方法將其反註冊,否則如果實現QMTINYDL::IQMTinyDLSink介面的外部物件提前銷燬,極易導致下載庫Crash,這裡沒有使用引用計數來管理各個物件主要是基於簡單輕便的考慮。
使用舉例
1、標頭檔案。
class CDeltaDlder
:public QMTINYDL::IQMTinyDLSink
,public DeltaTaskBase
{
public:
CDeltaDlder(void);
~CDeltaDlder(void);
……
// -------------------------------------------------------------------------
// IQMTinyDLSink
//
virtual void OnTinyDLComplete(LONG lTaskid, void* pBuffer, DWORD dwSize,DWORD dwErr, PVOID pContext);
virtual void OnTinyDLProgress(LONG lTaskID, DWORD dwLen, DWORDdwTotalLen, PVOID pContext) {}
virtual void OnTinyDLError(LONG lTaskID,DWORD dwErr, PVOID pContext) {}
……
private:
QMTINYDL::IQMTinyDLMgr* m_pHttpDlder;
……
};
2、實現檔案
BOOL CDeltaDlder::Init()
{
……
BOOLbRet = FALSE;
if(!m_pHttpDlder)
{
m_pHttpDlder = QMTINYDL::CreateQMTinyDLMgr();
}
if(!m_pHttpDlder)
{
LOG_ERROR_PRINTF(_T("[%s]QMTINYDL::CreateQMTinyDLMgr Error"), __FUNCTIONW__);
bRet= FALSE;
}
else
{
bRet= TRUE;
}
returnbRet;
}
VOID CDeltaDlder::UnInit()
{
if (m_pHttpDlder)
{
LOG_COMMON_PRINTF(L"[%s] UnInit()", __FUNCTIONW__);
m_pHttpDlder->UnregisterCallback(this);
QMTINYDL::DestroyQMTinyDLMgr(m_pHttpDlder);
m_pHttpDlder = NULL;
}
}
void CDeltaDlder::OnTinyDLComplete(LONGlTaskid, void* pBuffer, DWORD dwSize, DWORD dwErr, PVOID pContext)
{
utils::CCriticalSection::Ownero(m_csDL);
BOOL bDoTry = FALSE;
if(QMTINYDL::ERRORCODE_NULL != dwErr && lTaskid) //
{
……
}
……
}
……
DWORD dwTaskId = m_pHttpDlder->StartRangeTask(this, m_strDldUrl,itr->strDldFileName, itr->dw64DldOffset, itr->dw64DldLength);
……