• Qt 案例 使用QNetworkReply或者URLDownloadToFile 下载http、https资源到本地路径


    Qt 使用QNetworkReply或者URLDownloadToFile两种不同方式下载http、https链接资源文件,并且获取下载进度。

    一、 使用 URLDownloadToFile 下载

    使用【 URLDownloadToFile 】下载数据,只需调用函数,如果需要显示进度信息也只需要继承【IBindStatusCallback】类,通过重写OnProgress方法就能获取到及时进度:
    头文件调用:

    	#include 
    	#include 
    	// 下面这个也是
    	#pragma comment(lib, "Urlmon.lib")
    	
    	// HRESULT URLDownloadToFile(
        //         LPUNKNOWN            pCaller,
        //         LPCTSTR              szURL,
        //         LPCTSTR              szFileName,
      	//_Reserved_ DWORD                dwReserved,
        //         LPBINDSTATUSCALLBACK lpfnCB
    	//);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    调用:

        HRESULT ret =URLDownloadToFile(nullptr,_httpurl,_filepath,0,(LPBINDSTATUSCALLBACK)this);
        if (ret != S_OK)
            return false; // 下载失败
    
    • 1
    • 2
    • 3

    继承IBindStatusCallback主要需要实现以下方法,其中通过重写OnProgress方法获取到下载进度,通过返回S_OKE_ABORT判断是否继续下载文件:

    class DOWNFILES:public QObject,public IBindStatusCallback
    {
    	HRESULT __stdcall QueryInterface(const IID &, void **) { return E_NOINTERFACE; }
        ULONG STDMETHODCALLTYPE AddRef(void) { return 1; }
        ULONG STDMETHODCALLTYPE Release(void) { return 1; }
        HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, IBinding *pib) { return E_NOTIMPL; }
        virtual HRESULT STDMETHODCALLTYPE GetPriority(LONG *pnPriority) { return E_NOTIMPL; }
        virtual HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved){ return S_OK; }
        virtual HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, LPCWSTR szError) { return E_NOTIMPL; }
        virtual HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD *grfBINDF, BINDINFO *pbindinfo) { return E_NOTIMPL; }
        HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed){ return E_NOTIMPL; }
        virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, IUnknown *punk) { return E_NOTIMPL; }
    
        //    virtual HRESULT __stdcall OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
        //    { if (ulProgressMax != 0) { double *percentage = new double(ulProgress * 1.0 / ulProgressMax * 100); //将percentage 发送给显示 delete percentage; } return S_OK;
        ///进度
        HRESULT OnProgress( unsigned long ulProgress, unsigned long ulProgressMax,  unsigned long ulStatusCode, LPCWSTR  szStatusText) ;
    	//{if(!THREAD_IS_SHOULD_QUIT){ return S_OK;} else {return E_ABORT;}}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意: 使用URLDownloadToFile 下载数据时会在
    【(参考路径)C:\Users\admin\AppData\Local\Microsoft\Windows\INetCache\IE】目录下生成缓存文件,如果是小文件影响不大,如果超过1G,系统盘很容易就没有使用空间。
    也可以使用 DeleteUrlCacheEntry(URL链接) 或删除参考路径下的文件,但可能有其他问题
    不建议使用这种方式下载。

    二、 使用 QNetworkReply 下载

    使用【QNetworkReply】下载数据,主要通过readyReaddownloadProgress信号保存数据和获取进度。如果是https链接还需要下载QSslSocket相关库。
    pro头添加:

    QT += network
    
    • 1

    URL下载资源数据校验参考:
    可通过修改RawHeader的Range参数实现下载的断点继续下载功能。
    通过返回的Last-Modified参考是否是最新文件,
    通过返回的Content-Range获取文件大小。
    添加QNetworkRequest 的重定向防止部分链接是跳转Https资源

    #include 
    #include 
    #include 
    #include 
    
    //! url 
    //! filesize 获取文件大小
    //! outime 链接超时时间
    bool QTDOWNFILES::urlData_Validation(QString url,quint64& filesize,int outime)
    {
        bool IsSuccess=false;
        qDebug()<<"[url] 数据效验:";
        QNetworkRequest request;
        request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);   //启动重定向
        qDebug()<<"[urlData_Validation] "<<url;
        ///获取前500个字节效验文件是否过期
        request.setRawHeader("Range","bytes=0-499");
        request.setUrl(QUrl(url));
        //超时时间
        QTimer * out_timer = new QTimer();
        QEventLoop eventLoop;
        QNetworkAccessManager* manager=new QNetworkAccessManager();
        QNetworkReply *reply =manager->get(request);
        ///线程超时
        QObject::connect(out_timer,&QTimer::timeout,reply,&QNetworkReply::abort,Qt::UniqueConnection);
        ///只需要加载一次
        QObject::connect(reply, SIGNAL(readyRead()),&eventLoop, SLOT(quit()));
        out_timer->start(outime);
        eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
        out_timer->stop();
        if(reply->error() != QNetworkReply::NoError)
        {
            IsSuccess=false;
        }
        if(reply->hasRawHeader("Content-Range"))
        {
            QVariant contentrange = reply->rawHeader("Content-Range");
            qDebug()<<"[Content-Range] "<<contentrange;
            QString size=contentrange.toString().mid((0,contentrange.toString().indexOf("/")+1));
            qDebug()<<"Size : "<<size;
            filesize=size.toLongLong();
            IsSuccess=true;
        }
    
        //    if(reply->hasRawHeader("Last-Modified"))
        //    {
        //        QVariant contentrange = reply->rawHeader("Last-Modified");
        //        QString LastModified=contentrange.toString();
        //        qDebug()<<"LastModified : "<
        //    }
        manager->clearConnectionCache();
        manager->clearAccessCache();
        reply->abort();
        reply->close();
        return IsSuccess;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    绑定readyRead信号获取数据,和获取进度:

    void QTDOWNFILES::WriteFileData()
    {
     	if(reply!=nullptr && file!=nullptr)
        {
        	//file 打开的QFILE类型文件
            QByteArray temparray= reply->readAll();
            if(temparray.length()>0)
                file->write(temparray);
            temparray.clear();
        }
    }
    
    void QTDOWNFILES::HaveFulfilled(qint64 bytesRead,qint64 totalBytes)
    {
        emit ToFulfilled(bytesRead,totalBytes);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.注意: 使用QNetworkReply 下载时为了防止界面卡顿而使用多线程时,可以将QNetworkReply 的方法实现放入QOBJECT类中,在使用一个线程类来调用QOBJECT类中的方法,这样就避免了写入文件线程不同步的问题。也避免使用写锁。
    2.SSL问题: HTTPS资源文件需要使用SSL库。
    否则出现异常提示:qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed
    参考链接:
    1.Qt 解决qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed问题
    2.解决qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed

    三、 打包好的可执行程序示例下载

    详见文章绑定资源;
    在这里插入图片描述

    在这里插入图片描述

    四、 会员或订阅专栏下载源码

    Qt开发项目案例-以及部分示例的源码下载链接

  • 相关阅读:
    activemq学习笔记
    微服务(十六)——Seata 分布式事务框架
    痞子衡嵌入式:聊聊系统看门狗WDOG1在i.MXRT1xxx系统启动中的应用及影响
    基于粒子群优化的BP神经网络(分类应用) - 附代码
    springboot+个人博客 毕业设计-附源码191613
    深度强化学习应用实践技巧
    react 网页/app复制分享链接到剪切板,分享到国外各大社交平台,通过WhatsApp方式分享以及SMS短信方式分享链接内容
    我的名字叫大数据:第5章 我如何思考?
    JVM(6)
    【一起学Rust】Rust包管理工具Cargo初步了解
  • 原文地址:https://blog.csdn.net/qq_35554617/article/details/133958086