zoukankan      html  css  js  c++  java
  • c++のurlmon实现下载文件并进度回调

    主文件:
    #include "stdafx.h"
    #include <UrlMon.h>
    #pragma comment(lib, "urlmon.lib")
    #include <tchar.h>
    #include "cbindCallBack.h"
    #include "iostream"
    #include <CString>
    
    int main()
    {
    
        //在url后添加随机数,防止从IE缓存中读取。url后加随机数不会影响下载的。  
        //如果想要从缓存中提取那么就把下面的注释掉  
    
        DWORD rand= GetTickCount();
        
        CBindCallback cbc;
        HRESULT hr=URLDownloadToFile(NULL, _T("http://dldir1.qq.com/qqfile/qq/QQ8.9/19983/QQ8.9.exe"), _T("e:\download\qq8.9.exe"), NULL, &cbc);
        if (hr==S_OK)
        {
            std::cout << "下载完成!" << std::endl;
            system("e:\download\qq8.9.exe");
        }
        else if (hr== E_OUTOFMEMORY)
        {
            std::cout << "缓冲区长度无效,或内存不足,无法完成操作!" << std::endl;
        }
        else if (hr== E_OUTOFMEMORY)
        {
            std::cout << "指定的资源或回调接口无效!" << std::endl;
        }
    
        getchar();
        return 0;
    }
    
    
    cbindCallback.h
    
    #pragma once
    #include "stdafx.h"
    #include <UrlMon.h>
    #pragma comment(lib, "urlmon.lib")
    #include <tchar.h>
    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; }
            
            //OnProgress在这里  
            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; }
    };
    
    
    cbindCallBack.cpp
    
    #include "stdafx.h"
    #include "cbindCallBack.h"
    #include "iostream"
    using namespace std;
    
    //只需实现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);*/
        cout << "文件大小为:" << ulProgressMax /1024/1024 << "MB"<<endl;
        cout << ulProgress/1024/1024<<"MB" << endl;
        cout << "已下载:" << ulProgressMax*100.0/ulProgressMax << "%" << endl;
        return S_OK;
    }

    注意事项:

    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的设置,完成下载,不用考虑代理情况。

     

     

    关闭
  • 相关阅读:
    Python3 字符串格式化
    TypeError: Object of type 'int32' is not JSON serializable
    论文学习——《Learning to Compose with Professional Photographs on the Web》 (ACM MM 2017)
    python中PIL.Image,OpenCV,Numpy图像格式相互转换
    python 在列表,元组,字典变量前加*号
    python指定概率随机取值 理解np.random.seed()
    论文学习——《Good View Hunting: Learning Photo Composition from Dense View Pairs》
    目标检测知识杂点
    VGG16学习笔记
    python png与jpg的相互转换
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/6323080.html
Copyright © 2011-2022 走看看