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

     

     

    关闭
  • 相关阅读:
    防火墙透明模式
    HP管理工具System Management Homepage安装配置
    kbmmw 中JSON 中使用SQL 查询
    kbmmw 中JSON 操作入门
    第一个kbmmw for Linux 服务器
    kbmmw 5.02发布
    kbmmw 5.01 发布
    使用delphi 10.2 开发linux 上的Daemon
    使用unidac 在linux 上无驱动直接访问MS SQL SERVER
    使用delphi 10.2 开发linux 上的webservice
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/6323080.html
Copyright © 2011-2022 走看看