URLDownloadToFile下载文件文章两篇

来源:互联网 发布:xlsx 另存为xml数据 编辑:程序博客网 时间:2024/05/22 09:51

From:http://hi.baidu.com/qazssaeooiciqsr/item/eaeb813a284ef0677c034b85


最近使用 MFC的CInternetSession类下载文件时遇到一些问题, CInternetSession::OpenUrl 这个方法在使用个别代理的网络环境或者网速慢得要死的情况下,它会抛一个连接超时的异常,虽然catch 这个异常可以避免runtime error ,但下载也中止了。不过MSDN中提到SetOption可以设置超时时间,结果很悲剧的一次又一次的依然超时,后来才看到网上说SetOption无法如帮助文档中说的那样可以设置超时时间是MS已经确认的bug了,官方的解决方案是使用wininet系列函数或者将Openurl这部分代码放到一个线程里,如果线程超时就释放资源,关闭句柄,但这个也只能是防止内存泄露,对已经中断的下载依然于事无补。

URLDownloadToFile这个函数,我一开始只是用它来下载配置文件,主要考虑到如果下载较大的文件这个函数会阻塞很长时间,全部下载完毕后才返回,而且无法显示下载进度,今天才知道自己火星了,人家的最后一个参数提供了相关的接口(以前看都不看直接传NULL),查了些资料总算搞清楚怎么用了,记录一下。

1。创建一个IBindStatusCallback的派生类,声明IBindStatusCallback的8个方法。由于IBindStatusCallback继承自IUnknown,所以还要声明IUnknown的3个方法。具体方法的原型声明可以参照MSDN

2。可以控制显示进度条的是IBindStatusCallback::Onprogress,只要实现这个方法就行,IBindStatusCallback的其他7个方法IE是不会调用的,直接告诉IE这个我没实现,通通 return E_NOTIMPL    (not implemented)。另外IUnknown的AddRef 和 Release 分别是给调用接口增加引用计数 和 减少引用计数的,也用不到直接都返回0就可以了,IUnknown的另一个方法 QueryInterface也 return E_NOTIMPL。

3。派生类创建好之后,就很简单了,直接给URLDownloadToFile的最后一个参数传个指向派生类实例的指针就大功告成了

我定义了一个CBindCallback类,类的声明:

class CBindCallback : public IBindStatusCallback  
{
public:
CBindCallback();
virtual ~CBindCallback();

//接受显示进度窗口的句柄
     CUrlDownloadToFileCallbackTestDlg* m_pdlg;

//IBindStatusCallback的方法。除了OnProgress     外的其他方法都返回E_NOTIMPL 
    
STDMETHOD(OnStartBinding)
( DWORD dwReserved,
IBinding __RPC_FAR *pib)
{ return E_NOTIMPL; }

STDMETHOD(GetPriority)
( LONG __RPC_FAR *pnPriority)
{ return E_NOTIMPL; }

STDMETHOD(OnLowResource)
( DWORD reserved)
{ return E_NOTIMPL; }

STDMETHOD(OnProgress)
( ULONG ulProgress,    
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR wszStatusText);

STDMETHOD(OnStopBinding)
( HRESULT hresult,
LPCWSTR szError)
{ return E_NOTIMPL; }

STDMETHOD(GetBindInfo)
( DWORD __RPC_FAR *grfBINDF,
BINDINFO __RPC_FAR *pbindinfo)
{ return E_NOTIMPL; }

STDMETHOD(OnDataAvailable)
( DWORD grfBSCF,
DWORD dwSize,
FORMATETC __RPC_FAR *pformatetc,
STGMEDIUM __RPC_FAR *pstgmed)
{ return E_NOTIMPL; }

STDMETHOD(OnObjectAvailable)
( REFIID riid,
IUnknown __RPC_FAR *punk)
{ return E_NOTIMPL; }

// IUnknown方法.IE 不会调用这些方法的

STDMETHOD_(ULONG,AddRef)()
{ return 0; }

STDMETHOD_(ULONG,Release)()
{ return 0; }

STDMETHOD(QueryInterface)
( REFIID riid,
void __RPC_FAR *__RPC_FAR *ppvObject)
{ return E_NOTIMPL; }
};

只需实现OnProgress方法,类的实现:
CBindCallback::CBindCallback()
{

}

CBindCallback::~CBindCallback()
{

}

//////仅实现OnProgress成员即可

LRESULT CBindCallback::OnProgress(ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulSatusCode,
LPCWSTR szStatusText)
{
CProgressCtrl* m_prg = (CProgressCtrl*)m_pdlg->GetDlgItem(IDC_PROGRESS);
m_prg->SetRange32(0,ulProgressMax);
m_prg->SetPos(ulProgress);

CString szText;
szText.Format("已下载%d%%", (int)(ulProgress * 100.0 / ulProgressMax));
(m_pdlg->GetDlgItem(IDC_STATUS))->SetWindowText(szText);

return S_OK;
}


调用URLDownloadToFile下载即可
void CUrlDownloadToFileCallbackTestDlg::DownloadThread()
{
CBindCallback cbc;
cbc.m_pdlg = this;
CString szURL;
m_url.GetWindowText(szURL);

m_url.EnableWindow(FALSE);
(this->GetDlgItem(IDC_START))->EnableWindow(FALSE);

//在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。

CString szRand;
szRand.Format(_T("?skq=%d"),GetTickCount());
szUrl += szRand;

if(S_OK == URLDownloadToFile(NULL,szURL,szPath,0,&cbc))
MessageBox("finished");

}

Some Tips:

1。下载代码最好放到一个线程里,否则URLDownloadToFile下载过程中等待返回时会阻塞,使UI失去响应。
2。OnProgress返回S_OK表示正常,还可以通过返回E_ABORT使下载中断,所以可以设置个超时时间,如果超时的话,就让OnProgress返回E_ABORT。另外下次再开始从同一个url下载同一个文件时会直接由IE缓存中读取已下载的部分,达到“断点续传”的效果。

3。实际测试过程中发现URLDownloadToFile读IE缓存中已经下载的文件会有很大的安全隐患,如果哪次下载的文件发生问题,那么在不清除缓存的情况下,这个函数以后会一直读取损坏的文件而不重新下载。网上搜了一下解决方案,大概有三种:

a.下载前用FindFirstUrlCacheEntry,FindNextUrlCacheEntry,DeleteUrlCacheEntry清除cache,这个代码网上很多。

b.重载IBindStatusCallback的GetBindInfo方法,指定BINDF_GETNEWESTVERSION和BINDF_NOWRITECACHE属性,但是我测试发现即使指定这两个属性UrlDownloadToFile还是会很执着的读缓存,郁闷。

c.还有一种方法比较猥琐,在要下载的文件地址后加一个随机字符串,这样既不会影响正常下载(下载时会被指向正确的地址)而且由于每次传给URLDownloadToFile的url都不同,在cache中没有地址匹配的文件,所以会重新下载。上面的代码就使用了这种方法,个人感觉比较省事而且经测试有效。

4。CBindCallback有个成员变量用来传递进度条所在的窗口句柄m_pdlg,当然这个也可以用其他方式实现。
5。URLDownloadToFile的好处在于它会自动使用IE的设置,完成下载,不用考虑代理情况。



From:http://blog.csdn.net/aniven/article/details/1636760


函数定义 :

[cpp] view plaincopyprint?
  1. HRESULT URLDownloadToFile(   
  2.    LPUNKNOWN pCaller,   
  3.    LPCSTR szURL,   
  4.    LPCSTR szFileName,   
  5.    DWORD dwReserved,   
  6.    LPBINDSTATUSCALLBACK lpfnCB  
  7. );  

参数说明: 
pCaller : 仅当调用者是一个ActiveX对象才使用,对于一个非ActiveX对象的应用程序这个参数应该为NULL
szURL : 为要下载的绝对URL 文件名,这个参数不能为空。 
szFileName : 包含创建的目标文件名  
dwReserved : 必须为零 
lpfnCB : 一个指向 IBindStatusCallback 接口的指针, IE通过它向你通知下载的进度。

利用URLDownloadToFile()下载文件可以分下面几个步骤: 
1. 提供一个要下载的URL文件名。 
2. 构造一个目标创建文件的完整路径含文件名。 
3. 创建一个IBindStatusCallback派生类,编写OnProgress()重载函数。 
4. 派生一个类对象的实例。
5. 调用URLDownLoad 函数,由于这个函数调用是同步的,因此你最好在一个工作者线程中调用这个函数。
6. 在OnProgress函数中,提供任何你需要的进度指示信息和其它界面,函数返回值告诉IE是继续下载或者是放弃下载。

使用IbindStatusCallback 
IBindStatusCallback 回调接口共有8个方法,但是你只需关心OnProgress(). 其它方法只需返回E_NOTIMPL。

OnProgress 实现的规范是: 
[cpp] view plaincopyprint?
  1. HRESULT OnProgress(   
  2.    ULONG ulProgress,   
  3.    ULONG ulProgressMax,   
  4.    ULONG ulStatusCode,   
  5.    LPCWSTR szStatusText  
  6. );  

ulProgress
   到目前为止已经下载的字节数。 
ulProgressMax
   要下载的文件大小,0表示大小未知,需要注意的是,这个值仅在OnProgress调用期间变化,所以你不能把它保存到一个静态变量中,你应该在每次调用时检查这个值。  
ulStatusCode
   状态标志,这个值可以为下列一些值: 
   BINDSTATUS_BEGINDOWNLOADCOMPONENTS
   BINDSTATUS_INSTALLINGCOMPONENTS
   BINDSTATUS_ENDDOWNLOADCOMPONENTS
szStatusText
   图形界面中使用的字符串,由IE 提供,这个变量可能为NULL,在使用前应该检查这个变量。 

OnProgress()返回 S_OK 告诉IE要继续下载, E_ABORT则表示放弃下载。

以下是IBindStatusCallback的派生类写法 :

//头文件

[cpp] view plaincopyprint?
  1. //---------------------------------------------------------------------------  
  2. #ifndef CallbackH  
  3. #define CallbackH  
  4. #include <Urlmon.h>  
  5. #include "DownThread.h"  
  6. //---------------------------------------------------------------------------  
  7. class TCallback : public IBindStatusCallback  
  8. {  
  9. DWORD m_cRef;  
  10. //IBinding *m_pbinding;  
  11. private:  
  12.     STDMETHODIMP QueryInterface(REFIID riid,void **ppv);  
  13.     STDMETHODIMP_(ULONG) AddRef();  
  14.     STDMETHODIMP_(ULONG) Release();  
  15.     STDMETHODIMP GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo);  
  16.     STDMETHODIMP GetPriority(LONG *nPriority);  
  17.     STDMETHODIMP OnDataAvailable(DWORD grfBSCF,DWORD dwSize,  
  18.         FORMATETC *formatetc,STGMEDIUM *stgmed);  
  19.     STDMETHODIMP OnLowResource(DWORD reserved);  
  20.     STDMETHODIMP OnObjectAvailable(REFIID iid,IUnknown *punk);  
  21.     STDMETHODIMP OnStartBinding(DWORD dwReserved,IBinding *pib);  
  22.     STDMETHODIMP OnStopBinding(HRESULT hresult,LPCWSTR szError);  
  23.     STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax,  
  24.         ULONG ulStatusCode, LPCWSTR szStatusText);  
  25. public:  
  26.     TDownload *D;  
  27.     TCallback() {m_cRef = 1;/*m_pbinding = NULL*/};  
  28.     //~TCallback() {if (m_pbinding) m_pbinding->Release();};  
  29. };  
  30. #endif  


//CPP文件

[cpp] view plaincopyprint?
  1. //---------------------------------------------------------------------------  
  2. #include "Callback.h"  
  3. //---------------------------------------------------------------------------  
  4. STDMETHODIMP TCallback::QueryInterface(REFIID riid,void **ppv)  
  5. {  
  6.     *ppv = NULL;  
  7.     if (riid==IID_IUnknown || riid==IID_IBindStatusCallback) {  
  8.         *ppv = this;  
  9.         AddRef();  
  10.         return S_OK;  
  11.     }  
  12.     return E_NOINTERFACE;  
  13. }  
  14. STDMETHODIMP_(ULONG) TCallback::AddRef()  
  15. {  
  16.     return m_cRef++;  
  17. }  
  18. STDMETHODIMP_(ULONG) TCallback::Release()  
  19. {  
  20.     if(--m_cRef==0) {  
  21.         delete this;  
  22.         return 0;  
  23.     }  
  24.     return m_cRef;  
  25. }  
  26.   
  27. STDMETHODIMP TCallback::GetBindInfo(DWORD *grfBINDF,BINDINFO *bindinfo)  
  28. {  
  29.     return E_NOTIMPL;  
  30. }  
  31. STDMETHODIMP TCallback::GetPriority(LONG *nPriority)  
  32. {  
  33.     return E_NOTIMPL;  
  34. }  
  35. STDMETHODIMP TCallback::OnDataAvailable(DWORD grfBSCF,DWORD dwSize,  
  36.     FORMATETC *formatetc,STGMEDIUM *stgmed)  
  37. {         
  38.     return E_NOTIMPL;  
  39. }  
  40. STDMETHODIMP TCallback::OnLowResource(DWORD reserved)  
  41. {  
  42.     return E_NOTIMPL;  
  43. }  
  44. STDMETHODIMP TCallback::OnObjectAvailable(REFIID iid,IUnknown *punk)  
  45. {  
  46.     return E_NOTIMPL;  
  47. }  
  48. STDMETHODIMP TCallback::OnStartBinding(DWORD dwReserved,IBinding *pib)  
  49. {  
  50.     return E_NOTIMPL;  
  51. }  
  52. STDMETHODIMP TCallback::OnStopBinding(HRESULT hresult,LPCWSTR szError)  
  53. {  
  54.     return E_NOTIMPL;  
  55. }  
  56. STDMETHODIMP TCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax,  
  57.     ULONG ulStatusCode, LPCWSTR szStatusText)  
  58. {             
  59.     AnsiString Status;  
  60.     switch (ulStatusCode)  
  61.     {  
  62.       case BINDSTATUS_FINDINGRESOURCE : Status = "Finding resource " + AnsiString(szStatusText); break;  
  63.       case BINDSTATUS_CONNECTING : Status = "Connecting to " + AnsiString(szStatusText); break;  
  64.       case BINDSTATUS_REDIRECTING : Status = "Redirecting..."break;  
  65.       case BINDSTATUS_BEGINDOWNLOADDATA : Status = "Start to download " + AnsiString(szStatusText); break;   
  66.       case BINDSTATUS_DOWNLOADINGDATA : Status = "Downloading..."break;  
  67.       case BINDSTATUS_ENDDOWNLOADDATA : Status = "Complete downloading " + AnsiString(szStatusText); break;  
  68.       case BINDSTATUS_BEGINDOWNLOADCOMPONENTS : Status = "Start to download components"break;  
  69.       case BINDSTATUS_INSTALLINGCOMPONENTS : Status = "Installing components..." ; break;  
  70.       case BINDSTATUS_ENDDOWNLOADCOMPONENTS : Status = "Complete downloading components"break;  
  71.       case BINDSTATUS_USINGCACHEDCOPY : Status = "Copying form buffer..."break;  
  72.       case BINDSTATUS_SENDINGREQUEST : Status = "Sending request..."break;  
  73.       case BINDSTATUS_CLASSIDAVAILABLE : Status = "Class ID is available"break;  
  74.       case BINDSTATUS_MIMETYPEAVAILABLE : Status = "MIME type is available"break;  
  75.       case BINDSTATUS_CACHEFILENAMEAVAILABLE : Status = "Cache file name is available"break;  
  76.       case BINDSTATUS_BEGINSYNCOPERATION : Status = "Start sync operation"break;  
  77.       case BINDSTATUS_ENDSYNCOPERATION : Status = "Complete sync operation"break;  
  78.       case BINDSTATUS_BEGINUPLOADDATA : Status = "Start to upload data"break;  
  79.       case BINDSTATUS_UPLOADINGDATA : Status = "Uploading data"break;  
  80.       case BINDSTATUS_ENDUPLOADDATA : Status = "Complete Uploading data"break;  
  81.       case BINDSTATUS_PROTOCOLCLASSID : Status = "Protocol class ID is available"break;  
  82.       case BINDSTATUS_ENCODING : Status = "Encoding..."break;  
  83.       case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : Status = "Verified MIME is available"break;  
  84.       case BINDSTATUS_CLASSINSTALLLOCATION : Status = "Class install location"break;  
  85.       case BINDSTATUS_DECODING : Status = "Decoding..."break;  
  86.       case BINDSTATUS_LOADINGMIMEHANDLER : Status = "Loading MIME handler"break;  
  87.       case BINDSTATUS_CONTENTDISPOSITIONATTACH : Status = "Content disposition attach"break;  
  88.       case BINDSTATUS_FILTERREPORTMIMETYPE : Status = "Filter report MIME type"break;  
  89.       case BINDSTATUS_CLSIDCANINSTANTIATE : Status = "Clsid can instantiate"break;  
  90.       case BINDSTATUS_IUNKNOWNAVAILABLE : Status = "Unknown available"break;  
  91.       case BINDSTATUS_DIRECTBIND : Status = "Direct bind"break;  
  92.       case BINDSTATUS_RAWMIMETYPE : Status = "MIME type of the resource, before any code sniffing is done"break;  
  93.       case BINDSTATUS_PROXYDETECTING : Status = "Detecting proxy..."break;  
  94.       case BINDSTATUS_ACCEPTRANGES : Status = "Valid types of range requests for a resource"break;  
  95.       default : Status = "";  
  96.     }  
  97.     //在这里填入显示进度的代码。  
  98.     //如果要实现中断下载,最好在一个线程中调用URLDownloadToFile函数,  
  99.     //如下面代码中的 D 既是一个线程,设定DoCancel变量来决定是否取消下载。  
  100.     //DoProgress是线程中显示进度的函数。  
  101.     if (!Status.IsEmpty()) D->ShowMsg(Status);  
  102.     D->DoProgress(ulProgress,ulProgressMax,ulStatusCode);  
  103.     if (D->DoCancel) return E_ABORT;  
  104.     else return S_OK;  
  105. }  

使用范例:

[cpp] view plaincopyprint?
  1. #include <Urlmon.h> //还须加入urlmon.lib  
  2.   
  3. ...  
  4. TCallback Status; //回调类实例  
  5. URLDownloadToFile(NULL,"http://...","C://???",0,&Status);  


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 太阳熄灭了地球怎么办 饥荒晚上猎狗来怎么办 脑紊乱思维迟缓怎么办 儿童思维反应慢怎么办 w7系统反应慢怎么办 思维缓慢发呆书呆子怎么办 苹果手机网速慢怎么办? 在美国警察杀人怎么办? 征兵填写不知道怎么办 长虹子画面关怎么办 土地被村长霸占怎么办 半夜 手机卡掉了怎么办 淘宝号买家违规怎么办 镍氢电池没电了怎么办 地下室对讲机信号不好怎么办 cf不能说话了怎么办 cf没办法说话怎么办 cf对讲机没声音怎么办 交警用的对讲机怎么办 cf语音没有声音怎么办 去上海没有流量怎么办 去外地没流量怎么办 外地流量不够用怎么办 手机在外地流量怎么办 联通网卡在外地怎么办 套餐流量不够用怎么办 省内流量去省外怎么办 在外省流量不够怎么办 榨汁机开关坏了怎么办 健伍dvd没遥控器怎么办 佳能 显示屏关不了怎么办 iqos充电闪红灯怎么办 航班取消了乘客怎么办 摩托罗拉电话静音了怎么办 对讲机话筒坏了怎么办 摩托罗拉xt1570费电怎么办 主板没有rgb接口怎么办 对讲机频段没了怎么办 怀孕查出宫颈囊怎么办 办养殖场没地怎么办 宝宝睡觉衣服湿透怎么办