一个基于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);

……

猜你喜欢

转载自blog.csdn.net/magictong/article/details/80062195