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

关闭

时间: 2024-10-13 22:43:59

c++のurlmon实现下载文件并进度回调的相关文章

VC下载文件显示进度条

VC下载文件显示进度条 逗比汪星人2009-09-18上传 by Koma http://blog.csd.net/wangningyu http://download.csdn.net/detail/wangningyu/1674247

php下载文件显示进度(适用于CLI模式或长连接)

代码: <?php /* @desc:php下载文件显示进度 @param file 待下载文件名 @param name 存储到本地文件名 */ function downloadprogress($file,$name){ $dir = dirname($name); if(!is_dir($dir)){ mkdir($dir,0755,true); } ob_start(); set_time_limit(0); $fr = fopen ($file, "rb"); if

Android OkHttp + Retrofit 下载文件与进度监听

本文链接 下载文件是一个比较常见的需求.给定一个url,我们可以使用URLConnection下载文件. 使用OkHttp也可以通过流来下载文件. 给OkHttp中添加拦截器,即可实现下载进度的监听功能. 使用流来实现下载文件 代码可以参考:https://github.com/RustFisher/android-Basic4/tree/master/appdowloadsample 获取并使用字节流,需要注意两个要点,一个是服务接口方法的 @Streaming 注解,另一个是获取到Respo

UrlDownloadFile, 线程下载文件, 带进度条

unit FileDownLoadThread; interface uses Classes, SysUtils, Windows, ActiveX, UrlMon; const S_ABORT = HRESULT($80004004); type TFileDownLoadThread = class; TDownLoadProcessEvent = procedure(Sender: TFileDownLoadThread; Progress, ProgressMax: Cardinal)

VC下载文件 + 显示进度条

在codeproject里找了许久,发现这样一个VC下载文件并显示进度条的源码,于是添加了些中文注释: 1.下载线程函数: [cpp] view plain copy print? UINT DownloadFile(LPVOID pParam) { CWnd*           pwnd = AfxGetMainWnd(); CProgressCtrl*  m_Prog = (CProgressCtrl*)pwnd->GetDlgItem(IDC_PROGRESS1); CButton*  

webclient下载文件 带进度条

private void button1_Click(object sender, EventArgs e) { doDownload(textBox1.Text.Trim()); } private DateTime StartTime; private void doDownload(string url,string fileName="") { label1.Text = "正在下载:" + url;//label框提示下载文件 if (fileName.L

Python 给下载文件显示进度条和下载时间

其实实现它方法很简单,这里我写了个进度条的模块,其中还附带上了运行时间也就是下载时间了. 该模块调用了三个库:1.os 2.requests 3.time 话不多说,先上代码!!!. # 进度条模块 def progressbar(url,path): if not os.path.exists(path): # 看是否有该文件夹,没有则创建文件夹 os.mkdir(path) start = time.time() #下载开始时间 response = requests.get(url, st

Android下载文件的进度条提示(网络通信部分示例)

URL url = newURL(http://somewhere.com/some/webhosted/file); HttpURLConnectionurlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("GET"); urlConnection.setDoOutput(true); urlConnection.connect(); File SDCardRo

libcurl开源库在Win7 + VS2012环境下编译、配置详解 以及下载文件并显示下载进度 demo(转载)

转载:http://blog.csdn.net/fengshuiyue/article/details/39530093(基本教程) 转载:https://my.oschina.net/u/1420791/blog/198247 转载:http://www.cnblogs.com/flylong0204/p/4723155.html 转载:http://www.tuicool.com/articles/VNRzEbq 转载:http://blog.csdn.net/hei_ya/article/