1. 程式人生 > >一個基於WinHttp的輕量級的分片下載庫介紹

一個基於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

    // 功能     :下載結束回撥(

dwErr==0表示下載成功)

    // 返回值  : 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

    // 功能     :停止idlTaskID的下載任務

    // 返回值  : 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);

……