Qt实现HTTP精美下载功能带进度条哦

0 371
索鸟 2021-03-18
需要:0索币

//Downloader.h

#pragma once

#pragma execution_character_set("utf-8")

#include <QCoreApplication>

#include <QDir>

#include <QFile>

#include <QFileInfo>

#include <QNetworkAccessManager>

#include <QNetworkRequest>

#include <QNetworkReply>

#include <QSslError>

#include <QTimer>

#include <QUrl>

#include <QVector>

 

enum DownloadResult {

DR_SUCCESS,

DR_FAILURE,

DR_ERROR,

DR_TIMEOUT,

};

 

/*下载器类*/

class Downloader : public QObject

{

Q_OBJECT

public:

/*构造*/

Downloader();

 

/*析构*/

~Downloader();

 

/*获取最终错误*/

const QString& getLastError();

 

/*获取文件大小*/

const qint64 getFileSize(const QUrl& url, int times = 0);

 

/*下载*/

void download(const QUrl& url);

 

/*设置下载超时*/

void setTimeout(const int& timeout);

 

/*设置保存路径*/

void setSavePath(const QString& savePath);

 

/*获取应答*/

QNetworkReply* getReply();

 

/*所用时间,单位MS*/

const ulong getElapsedTime();

 

/*平均网速,单位KB*/

const float getAverageSpeed();

 

/*文件大小,单位MB*/

const float getFileSize();

signals:

/*结果信号*/

void resultSignal(const DownloadResult& result, const QString& error);

 

/*进度信号*/

void progressSignal(const qint64& receive, const qint64& total, const float& speed);

public slots:

/*完成槽*/

void finishedSlot(QNetworkReply* reply);

 

/*SSL错误槽*/

void sslErrorsSlot(const QList<QSslError>& errors);

 

/*进度槽*/

void progressSlot(qint64 recvBytes, qint64 totalBytes);

 

/*超时槽*/

void timeoutSlot();

protected:

/*设置最终错误*/

void setLastError(const QString& error);

 

/*保存文件名*/

const QString saveFileName(const QUrl& url);

 

/*保存到磁盘*/

bool saveToDisk(const QString& filename, QIODevice* data);

private:

QString m_lastError = "No Error";

 

QNetworkAccessManager m_manager;

 

QNetworkReply* m_reply = nullptr;

 

QTimer m_timer;

 

int m_timeout = 120000;

 

ulong m_startTime = 0;

 

ulong m_endTime = 0;

 

qint64 m_recvBytes = 0;

 

qint64 m_fileSize = 0;

 

QVector<float> m_speedV;

 

QString m_savePath = "Downloader";

};

 

/*应答超时类*/

class QReplyTimeout : public QObject

{

Q_OBJECT

public:

QReplyTimeout(QNetworkReply* reply, const int& timeout) : QObject(reply)

{

if (reply && reply->isRunning())

{

QTimer::singleShot(timeout, this, &QReplyTimeout::onTimeout);

}

}

private slots:

void onTimeout()

{

QNetworkReply* reply = static_cast<QNetworkReply*>(parent());

if (reply->isRunning() && reply->size() == 0)

{

reply->abort();

reply->deleteLater();

emit timeout();

}

}

signals:

void timeout();

};

//Downloader.cpp

#include "Downloader.h"

#include <windows.h>

 

Downloader::Downloader()

{

connect(&m_manager, &QNetworkAccessManager::finished, this, &Downloader::finishedSlot);

connect(&m_timer, &QTimer::timeout, this, &Downloader::timeoutSlot);

}

 

Downloader::~Downloader()

{

 

}

 

const QString& Downloader::getLastError()

{

return m_lastError;

}

 

const qint64 Downloader::getFileSize(const QUrl& url, int times)

{

qint64 size = -1;

do

{

QNetworkAccessManager manager;

QEventLoop loop;

QTimer timer;

 

QNetworkReply* reply = manager.head(QNetworkRequest(url));

if (!reply) continue;

 

QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);

QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);

 

timer.start(3000);

loop.exec();

 

if (reply->error() != QNetworkReply::NoError)

{

setLastError(reply->errorString());

continue;

}

QVariant var = reply->header(QNetworkRequest::ContentLengthHeader);

size = var.toLongLong();

reply->deleteLater();

break;

} while (times-- > 0);

return size;

}

 

void Downloader::download(const QUrl& url)

{

m_recvBytes = 0;

m_speedV.clear();

QNetworkRequest request(url);

QNetworkReply* reply = m_manager.get(request);

m_reply = reply;

connect(reply, &QNetworkReply::downloadProgress, this, &Downloader::progressSlot);

connect(reply, &QNetworkReply::sslErrors, this, &Downloader::sslErrorsSlot);

connect(new QReplyTimeout(reply, 3000), &QReplyTimeout::timeout, this, [&]() {emit resultSignal(DR_TIMEOUT, "连接超时"); });

m_startTime = GetTickCount();

}

 

void Downloader::setTimeout(const int& timeout)

{

m_timeout = timeout;

}

 

void Downloader::setSavePath(const QString& savePath)

{

m_savePath = savePath;

}

 

QNetworkReply* Downloader::getReply()

{

return m_reply;

}

 

const ulong Downloader::getElapsedTime()

{

return m_endTime;

}

 

const float Downloader::getAverageSpeed()

{

float sum = 0.0f;

for (auto& x : m_speedV)

sum += x;

return sum / m_speedV.size();

}

 

const float Downloader::getFileSize()

{

return (float)m_fileSize / 1024.0f / 1024.0f;

}

 

void Downloader::sslErrorsSlot(const QList<QSslError>& sslErrors)

{

foreach(const QSslError & error, sslErrors)

setLastError(error.errorString());

}

 

void Downloader::progressSlot(qint64 recvBytes, qint64 totalBytes)

{

m_fileSize = totalBytes;

float speed = (recvBytes - m_recvBytes) / 1024.0 * 8;

m_speedV.push_back(speed);

emit progressSignal(recvBytes, totalBytes, speed);

m_recvBytes = recvBytes;

}

 

void Downloader::timeoutSlot()

{

 

}

 

void Downloader::setLastError(const QString& error)

{

#ifdef QT_DEBUG

qDebug() << error;

#endif

m_lastError = error;

}

 

const QString Downloader::saveFileName(const QUrl& url)

{

QString path = url.path();

QString basename = QFileInfo(path).fileName();

if (basename.isEmpty()) basename = "NoNameFile";

 

QString savePath = QString(".\\%1\\").arg(m_savePath);

if (!QDir(savePath).exists())

{

QDir dir;

if (!dir.mkpath(savePath))

{

setLastError("创建下载目录失败");

return basename;

}

}

 

if (QFile::exists(savePath + basename))

{

int i = 0;

basename += '.';

while (QFile::exists(savePath + basename + QString::number(i))) ++i;

basename += QString::number(i);

}

return basename;

}

 

bool Downloader::saveToDisk(const QString& filename, QIODevice* data)

{

bool result = false;

do

{

QString savePath = QString(".\\%1\\").arg(m_savePath);

if (!QDir(savePath).exists())

{

QDir dir;

if (!dir.mkpath(savePath))

{

setLastError("创建下载目录失败");

break;

}

}

QFile file(savePath + filename);

if (!file.open(QIODevice::WriteOnly))

{

setLastError(file.errorString());

break;

}

file.write(data->readAll());

file.close();

result = true;

} while (false);

return result;

}

 

void Downloader::finishedSlot(QNetworkReply* reply)

{

m_endTime = GetTickCount() - m_startTime;

QUrl url = reply->url();

if (reply->error())

{

emit resultSignal(DR_ERROR, reply->errorString());

}

else

{

if (saveToDisk(saveFileName(url), reply))

emit resultSignal(DR_SUCCESS, "下载成功");

else

emit resultSignal(DR_FAILURE, "下载失败," + getLastError());

}

reply->deleteLater();

}

//DownloadDlg.ui,我的Qt版本5.7.0

 

<?xml version="1.0" encoding="UTF-8"?>

<ui version="4.0">

 <class>DownloadDlg</class>

 <widget class="QDialog" name="DownloadDlg">

  <property name="geometry">

   <rect>

    <x>0</x>

    <y>0</y>

    <width>492</width>

    <height>124</height>

   </rect>

  </property>

  <property name="windowTitle">

   <string>DownloadDlg</string>

  </property>

  <layout class="QVBoxLayout" name="verticalLayout">

   <item>

    <widget class="QLabel" name="titleLabel">

     <property name="font">

      <font>

       <family>华文行楷</family>

       <pointsize>20</pointsize>

       <weight>50</weight>

       <italic>false</italic>

       <bold>false</bold>

      </font>

     </property>

     <property name="text">

      <string>正在下载更新程序</string>

     </property>

     <property name="alignment">

      <set>Qt::AlignCenter</set>

     </property>

    </widget>

   </item>

   <item>

    <layout class="QHBoxLayout" name="horizontalLayout_3">

     <item>

      <widget class="QLabel" name="label">

       <property name="text">

        <string>文件大小/已下载:</string>

       </property>

      </widget>

     </item>

     <item>

      <widget class="QLabel" name="sizeLabel">

       <property name="text">

        <string>00.00MB/00.00MB</string>

       </property>

      </widget>

     </item>

     <item>

      <spacer name="horizontalSpacer">

       <property name="orientation">

        <enum>Qt::Horizontal</enum>

       </property>

       <property name="sizeHint" stdset="0">

        <size>

         <width>40</width>

         <height>20</height>

        </size>

       </property>

      </spacer>

     </item>

    </layout>

   </item>

   <item>

    <layout class="QHBoxLayout" name="horizontalLayout_2">

     <item>

      <widget class="QLabel" name="urlLabel">

       <property name="text">

        <string>http://download.osgeo.org/libtiff/old/tiff-3.7.0beta.zip</string>

       </property>

      </widget>

     </item>

     <item>

      <widget class="QLabel" name="speedLabel">

       <property name="text">

        <string>0.00Mb/s</string>

       </property>

       <property name="alignment">

        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>

       </property>

      </widget>

     </item>

    </layout>

   </item>

   <item>

    <layout class="QHBoxLayout" name="horizontalLayout">

     <item>

      <widget class="QProgressBar" name="progressBar">

       <property name="value">

        <number>0</number>

       </property>

       <property name="alignment">

        <set>Qt::AlignCenter</set>

       </property>

       <property name="textDirection">

        <enum>QProgressBar::TopToBottom</enum>

       </property>

      </widget>

     </item>

     <item>

      <widget class="QPushButton" name="abortBtn">

       <property name="text">

        <string>终止</string>

       </property>

      </widget>

     </item>

    </layout>

   </item>

  </layout>

 </widget>

 <layoutdefault spacing="6" margin="11"/>

 <resources/>

 <connections/>

</ui>

//DownloadDlg.h

 

#pragma once

#pragma execution_character_set("utf-8")

#include <QCoreApplication>

#include <QDir>

#include <QFile>

#include <QFileInfo>

#include <QNetworkAccessManager>

#include <QNetworkRequest>

#include <QNetworkReply>

#include <QSslError>

#include <QTimer>

#include <QUrl>

#include <QVector>

 

enum DownloadResult {

DR_SUCCESS,

DR_FAILURE,

DR_ERROR,

DR_TIMEOUT,

};

 

/*下载器类*/

class Downloader : public QObject

{

Q_OBJECT

public:

/*构造*/

Downloader();

 

/*析构*/

~Downloader();

 

/*获取最终错误*/

const QString& getLastError();

 

/*获取文件大小*/

const qint64 getFileSize(const QUrl& url, int times = 0);

 

/*下载*/

void download(const QUrl& url);

 

/*设置下载超时*/

void setTimeout(const int& timeout);

 

/*设置保存路径*/

void setSavePath(const QString& savePath);

 

/*获取应答*/

QNetworkReply* getReply();

 

/*所用时间,单位MS*/

const ulong getElapsedTime();

 

/*平均网速,单位KB*/

const float getAverageSpeed();

 

/*文件大小,单位MB*/

const float getFileSize();

signals:

/*结果信号*/

void resultSignal(const DownloadResult& result, const QString& error);

 

/*进度信号*/

void progressSignal(const qint64& receive, const qint64& total, const float& speed);

public slots:

/*完成槽*/

void finishedSlot(QNetworkReply* reply);

 

/*SSL错误槽*/

void sslErrorsSlot(const QList<QSslError>& errors);

 

/*进度槽*/

void progressSlot(qint64 recvBytes, qint64 totalBytes);

 

/*超时槽*/

void timeoutSlot();

protected:

/*设置最终错误*/

void setLastError(const QString& error);

 

/*保存文件名*/

const QString saveFileName(const QUrl& url);

 

/*保存到磁盘*/

bool saveToDisk(const QString& filename, QIODevice* data);

private:

QString m_lastError = "No Error";

 

QNetworkAccessManager m_manager;

 

QNetworkReply* m_reply = nullptr;

 

QTimer m_timer;

 

int m_timeout = 120000;

 

ulong m_startTime = 0;

 

ulong m_endTime = 0;

 

qint64 m_recvBytes = 0;

 

qint64 m_fileSize = 0;

 

QVector<float> m_speedV;

 

QString m_savePath = "Downloader";

};

 

/*应答超时类*/

class QReplyTimeout : public QObject

{

Q_OBJECT

public:

QReplyTimeout(QNetworkReply* reply, const int& timeout) : QObject(reply)

{

if (reply && reply->isRunning())

{

QTimer::singleShot(timeout, this, &QReplyTimeout::onTimeout);

}

}

private slots:

void onTimeout()

{

QNetworkReply* reply = static_cast<QNetworkReply*>(parent());

if (reply->isRunning() && reply->size() == 0)

{

reply->abort();

reply->deleteLater();

emit timeout();

}

}

signals:

void timeout();

};

//DownloadDlg.cpp

#include "DownloadDlg.h"

#include "Detection.h"

 

DownloadDlg::DownloadDlg(QWidget *parent)

: QDialog(parent)

{

ui.setupUi(this);

installEventFilter(this);

Misc::ThemeFactory::setBorderRadius(this);

connect(ui.abortBtn, &QPushButton::clicked, this, [&]() {m_manager->getReply()->abort(); });

resetNetwork();

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);

}

 

DownloadDlg::~DownloadDlg()

{

SAFE_DELETE(m_manager);

}

 

void DownloadDlg::download(const QString& title, const QString& url, const QString& path)

{

ui.progressBar->setValue(0);

ui.titleLabel->setText(title);

ui.urlLabel->setText(url);

m_manager->setSavePath(path);

m_manager->download(url);

}

 

bool DownloadDlg::getResult()

{

return m_result == DR_SUCCESS;

}

 

const ulong DownloadDlg::getElapsedTime()

{

return m_manager->getElapsedTime();

}

 

const float DownloadDlg::getAverageSpeed()

{

return m_manager->getAverageSpeed();

}

 

const float DownloadDlg::getFileSize()

{

return m_manager->getFileSize();

}

 

const QString& DownloadDlg::getLastError()

{

return m_lastError;

}

 

bool DownloadDlg::resetNetwork()

{

//Network access is disabled.Qt的BUG

SAFE_DELETE(m_manager);

m_manager = NO_THROW_NEW Downloader;

connect(m_manager, &Downloader::progressSignal, this, &DownloadDlg::progressSlot);

connect(m_manager, &Downloader::resultSignal, this, &DownloadDlg::resultSlot);

return m_manager != nullptr;

}

 

void DownloadDlg::setLastError(const QString& error)

{

m_lastError = error;

}

 

void DownloadDlg::mousePressEvent(QMouseEvent* event)

{

if (event->button() == Qt::LeftButton)

{

m_isPress = true;

m_point = event->globalPos();

}

}

 

void DownloadDlg::mouseReleaseEvent(QMouseEvent* event)

{

if (m_isPress)

{

m_isPress = false;

}

}

 

void DownloadDlg::mouseMoveEvent(QMouseEvent* event)

{

if (m_isPress)

{

int x = event->globalX() - m_point.x();

int y = event->globalY() - m_point.y();

m_point = event->globalPos();

move(this->x() + x, this->y() + y);

}

}

 

bool DownloadDlg::eventFilter(QObject* obj, QEvent* event)

{

if (event->type() == QEvent::KeyPress)

{

if (reinterpret_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape)

{

return true;

}

}

return QObject::eventFilter(obj, event);

}

 

void DownloadDlg::resultSlot(const DownloadResult& result, const QString& error)

{

setLastError(error);

m_result = result;

this->close();

}

 

void DownloadDlg::progressSlot(const qint64& receive, const qint64& total, const float& speed)

{

ui.sizeLabel->setText(QString().sprintf("%.2fMB/%.2fMB", (float)total / 1024 / 1024,

(float)receive / 1024 / 1024));

ui.speedLabel->setText(QString().sprintf("%4.2fKb/s", speed));

ui.progressBar->setValue(((float)receive / total) * 100);

}

注意点:


宏定义SAFE_DELETE原型


#define SAFE_DELETE(X)\


if (X)\


{\


delete X;\


X=nullpter;\


}


Misc::ThemeFactory::setBorderRadius(this);函数原型为头文件自己去百度找吧,不贴了.


class ThemeFactory {

    public:

        /*构造*/

        inline ThemeFactory() {}


        /*析构*/

        inline ~ThemeFactory() {}


        /*获取主题列表*/

        inline static const QStringList getThemeList()

        {

            return QStyleFactory::keys();

        }


        /*随机选择一个主题*/

        inline static void randomTheme()

        {

            setTheme(getThemeList().value(qrand() % getThemeList().size()));

        }


        /*设置主题*/

        inline static void setTheme(const QString& theme = QString("Fusion"))

        {

            qApp->setStyle(QStyleFactory::create(theme));

            QPalette palette;

            palette.setColor(QPalette::Window, QColor(53, 53, 53));

            palette.setColor(QPalette::WindowText, Qt::white);

            palette.setColor(QPalette::Base, QColor(15, 15, 15));

            palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));

            palette.setColor(QPalette::ToolTipBase, Qt::white);

            palette.setColor(QPalette::ToolTipText, Qt::white);

            palette.setColor(QPalette::Text, Qt::white);

            palette.setColor(QPalette::Button, QColor(53, 53, 53));

            palette.setColor(QPalette::ButtonText, Qt::white);

            palette.setColor(QPalette::BrightText, Qt::red);

            palette.setColor(QPalette::Highlight, QColor(142, 45, 197).lighter());

            palette.setColor(QPalette::HighlightedText, Qt::black);

            qApp->setPalette(palette);

        }


        /*设置边框为圆角*/

        inline static void setBorderRadius(QWidget* widget)

        {

            QBitmap bmp(widget->size());

            bmp.fill();

            QPainter p(&bmp);

            p.setPen(Qt::NoPen);

            p.setBrush(Qt::black);

            p.drawRoundedRect(bmp.rect(), 20, 20);

            widget->setMask(bmp);

        }

    };


最终效果展示:

回帖
  • 消灭零回复
相关主题
2020年最新最新Kubernetes视频教程(K8s)教程 2
程序员转型之制作网课变现,月入过万告别996 1
索鸟快传2.0发布啦 1
两个不同网络的电脑怎么实现文件的互相访问呢? 1
网盘多账号登录软件 1
Java实战闲云旅游项目基于vue+element-ui 1
单点登录技术解决方案基于OAuth2.0的网关鉴权RSA算法生成令牌 1
QT5获取剪贴板上文本信息QT设置剪贴板内容 1
springboot2实战在线购物系统电商系统 1
python web实战之爱家租房项目 1
windows COM实用入门教程 1
C++游戏开发之C++实现的水果忍者游戏 1
计算机视觉库opencv教程 1
node.js实战图书管理系统express框架实现 1
C++实战教程之远程桌面远程控制实战 1
相关主题
PHP7报A non well formed numeric value encountered 0
Linux系统下关闭mongodb的几种命令分享 0
mongodb删除数据、删除集合、删除数据库的命令 0
Git&Github极速入门与攻坚实战课程 0
python爬虫教程使用Django和scrapy实现 0
libnetsnmpmibs.so.31: cannot open shared object file 0
数据结构和算法视频教程 0
redis的hash结构怎么删除数据呢? 0
C++和LUA解析器的数据交互实战视频 0
mongodb errmsg" : "too many users are authenticated 0
C++基础入门视频教程 0
用30个小时精通C++视频教程可能吗? 0
C++分布式多线程游戏服务器开发视频教程socket tcp boost库 0
C++培训教程就业班教程 0
layui的util工具格式时间戳为字符串 0
C++实战教程之远程桌面远程控制实战 1
网络安全培训视频教程 0
LINUX_C++软件工程师视频教程高级项目实战 0
C++高级数据结构与算法视频教程 0
跨域问题很头疼?通过配置nginx轻松解决ajax跨域问题 0