Qt文件下载工具

news2024/11/23 2:47:53

在Qt中实现文件下载功能,通常可以通过多种方式来完成,包括使用 QNetworkAccessManager 和 QNetworkReply 类,或者使用更高级别的 QHttpMultiPart 类。以下是两种常见的实现方法:

方法1:使用 QNetworkAccessManager 和 QNetworkReply
创建 QNetworkAccessManager 对象:这个对象负责管理和处理网络请求。

发送 GET 请求:使用 QNetworkAccessManager 的 get 方法来发送一个GET请求到指定的URL。

接收数据:通过重写槽函数 readyRead 来接收数据。

保存文件:将接收到的数据写入到文件中。

处理完成:当下载完成后,重写 finished 槽函数来处理完成的逻辑。
请添加图片描述


#ifndef DOWNLOAD_DIALOG_H
#define DOWNLOAD_DIALOG_H

#include <QDir>
#include <QDialog>
#include <ui_Downloader.h>

namespace Ui {
class Downloader;
}

class QNetworkReply;
class QNetworkAccessManager;

/**
 * \brief Implements an integrated file downloader with a nice UI
 */
class Downloader : public QWidget
{
    Q_OBJECT

signals:
    void downloadFinished (const QString& url, const QString& filepath);

public:
    explicit Downloader (QWidget* parent = 0);
    ~Downloader();

    bool useCustomInstallProcedures() const;

    QString downloadDir() const;
    void setDownloadDir(const QString& downloadDir);

public slots:
    void setUrlId (const QString& url);
    void startDownload (const QUrl& url);
    void setFileName (const QString& file);
    void setUserAgentString (const QString& agent);
    void setUseCustomInstallProcedures (const bool custom);

private slots:
    void finished();
    void openDownload();
    void installUpdate();
    void cancelDownload();
    void saveFile (qint64 received, qint64 total);
    void calculateSizes (qint64 received, qint64 total);
    void updateProgress (qint64 received, qint64 total);
    void calculateTimeRemaining (qint64 received, qint64 total);

private:
    qreal round (const qreal& input);

private:
    QString m_url;
    uint m_startTime;
    QDir m_downloadDir;
    QString m_fileName;
    Ui::Downloader* m_ui;
    QNetworkReply* m_reply;
    QString m_userAgentString;
    bool m_useCustomProcedures;
    QNetworkAccessManager* m_manager;
};

#endif

#pragma execution_character_set("utf-8")
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QMessageBox>
#include <QNetworkReply>
#include <QDesktopServices>
#include <QNetworkAccessManager>

#include <math.h>

#include "Downloader.h"

static const QString PARTIAL_DOWN (".part");

Downloader::Downloader (QWidget* parent) : QWidget (parent)
{
    m_ui = new Ui::Downloader;
    m_ui->setupUi (this);
    /* Initialize private members */
    m_manager = new QNetworkAccessManager();

    /* Initialize internal values */
    m_url = "";
    m_fileName = "";
    m_startTime = 0;
    m_useCustomProcedures = false;

    /* Set download directory */
    m_downloadDir = QDir::homePath() + "/Downloads/";
    /* Configure the appearance and behavior of the buttons */
    m_ui->openButton->setEnabled (false);
    m_ui->openButton->setVisible (false);
    connect (m_ui->stopButton, SIGNAL (clicked()),
             this,               SLOT (cancelDownload()));
    connect (m_ui->openButton, SIGNAL (clicked()),
             this,               SLOT (installUpdate()));

    /* Resize to fit */
}

Downloader::~Downloader()
{
    delete m_ui;
    delete m_reply;
    delete m_manager;
}

/**
 * Returns \c true if the updater shall not intervene when the download has
 * finished (you can use the \c QSimpleUpdater signals to know when the
 * download is completed).
 */
bool Downloader::useCustomInstallProcedures() const
{
    return m_useCustomProcedures;
}

/**
 * Changes the URL, which is used to indentify the downloader dialog
 * with an \c Updater instance
 *
 * \note the \a url parameter is not the download URL, it is the URL of
 *       the AppCast file
 */
void Downloader::setUrlId (const QString& url)
{
    m_url = url;
}

/**
 * Begins downloading the file at the given \a url
 */
void Downloader::startDownload (const QUrl& url)
{
    /* Reset UI */
    m_ui->progressBar->setValue (0);
    m_ui->stopButton->setText (tr ("Stop"));
    m_ui->downloadLabel->setText (tr ("Downloading updates"));
    m_ui->timeLabel->setText (tr ("Time remaining") + ": " + tr ("unknown"));

    /* Configure the network request */
    QNetworkRequest request (url);
    if (!m_userAgentString.isEmpty())
        request.setRawHeader ("User-Agent", m_userAgentString.toUtf8());

    /* Start download */
    m_reply = m_manager->get (request);
    m_startTime = QDateTime::currentDateTime().toTime_t();

    /* Ensure that downloads directory exists */
    if (!m_downloadDir.exists())
        m_downloadDir.mkpath (".");

    /* Remove old downloads */
    QFile::remove (m_downloadDir.filePath (m_fileName));
    QFile::remove (m_downloadDir.filePath (m_fileName + PARTIAL_DOWN));

    /* Update UI when download progress changes or download finishes */
    connect (m_reply, SIGNAL (downloadProgress (qint64, qint64)),
             this,      SLOT (updateProgress   (qint64, qint64)));
    connect (m_reply, SIGNAL (finished ()),
             this,      SLOT (finished ()));
    connect (m_reply, SIGNAL (redirected       (QUrl)),
             this,      SLOT (startDownload    (QUrl)));

}

/**
 * Changes the name of the downloaded file
 */
void Downloader::setFileName (const QString& file)
{
    m_fileName = file;

    if (m_fileName.isEmpty())
        m_fileName = "QSU_Update.bin";
}

/**
 * Changes the user-agent string used to communicate with the remote HTTP server
 */
void Downloader::setUserAgentString (const QString& agent)
{
    m_userAgentString = agent;
}

void Downloader::finished()
{
    /* Rename file */
    QFile::rename (m_downloadDir.filePath (m_fileName + PARTIAL_DOWN),
                   m_downloadDir.filePath (m_fileName));

    /* Notify application */
    emit downloadFinished (m_url, m_downloadDir.filePath (m_fileName));

    /* Install the update */
    m_reply->close();
    installUpdate();
    setVisible (false);
}

/**
 * Opens the downloaded file.
 * \note If the downloaded file is not found, then the function will alert the
 *       user about the error.
 */
void Downloader::openDownload()
{
    if (!m_fileName.isEmpty())
        QDesktopServices::openUrl (QUrl::fromLocalFile (m_downloadDir.filePath (
                                                            m_fileName)));

    else {
        QMessageBox::critical (this,
                               tr ("Error"),
                               tr ("Cannot find downloaded update!"),
                               QMessageBox::Close);
    }
}

/**
 * Instructs the OS to open the downloaded file.
 *
 * \note If \c useCustomInstallProcedures() returns \c true, the function will
 *       not instruct the OS to open the downloaded file. You can use the
 *       signals fired by the \c QSimpleUpdater to install the update with your
 *       own implementations/code.
 */
void Downloader::installUpdate()
{
    if (useCustomInstallProcedures())
        return;

    /* Update labels */
    m_ui->stopButton->setText    (tr ("Close"));
    m_ui->downloadLabel->setText (tr ("Download complete!"));
    m_ui->timeLabel->setText     (tr ("The installer will open separately")
                                  + "...");

    /* Ask the user to install the download */
    QMessageBox box;
    box.setIcon (QMessageBox::Question);
    box.setDefaultButton   (QMessageBox::Ok);
    box.setStandardButtons (QMessageBox::Ok | QMessageBox::Cancel);
    box.setInformativeText (tr ("Click \"OK\" to begin installing the update"));
    box.setText ("<h3>" +
                 tr ("In order to install the update, you may need to "
                     "quit the application.")
                 + "</h3>");

    /* User wants to install the download */
    if (box.exec() == QMessageBox::Ok) {
        if (!useCustomInstallProcedures())
            openDownload();
    }

    /* Wait */
    else {
        m_ui->openButton->setEnabled (true);
        m_ui->openButton->setVisible (true);
        m_ui->timeLabel->setText (tr ("Click the \"Open\" button to "
                                      "apply the update"));
    }
}

/**
 * Prompts the user if he/she wants to cancel the download and cancels the
 * download if the user agrees to do that.
 */
void Downloader::cancelDownload()
{
    if (!m_reply->isFinished()) {
        QMessageBox box;
        box.setWindowTitle (tr ("Updater"));
        box.setIcon (QMessageBox::Question);
        box.setStandardButtons (QMessageBox::Yes | QMessageBox::No);
        box.setText (tr ("Are you sure you want to cancel the download?"));

        if (box.exec() == QMessageBox::Yes) {
            m_reply->abort();
        }
    }
}

/**
 * Writes the downloaded data to the disk
 */
void Downloader::saveFile (qint64 received, qint64 total)
{
    Q_UNUSED(received);
    Q_UNUSED(total);

    /* Check if we need to redirect */
    QUrl url = m_reply->attribute (
                QNetworkRequest::RedirectionTargetAttribute).toUrl();
    if (!url.isEmpty()) {
        startDownload (url);
        return;
    }

    /* Save downloaded data to disk */
    QFile file (m_downloadDir.filePath (m_fileName + PARTIAL_DOWN));
    if (file.open (QIODevice::WriteOnly | QIODevice::Append)) {
        file.write (m_reply->readAll());
        file.close();
    }
}


/**
 * Calculates the appropiate size units (bytes, KB or MB) for the received
 * data and the total download size. Then, this function proceeds to update the
 * dialog controls/UI.
 */
void Downloader::calculateSizes (qint64 received, qint64 total)
{
    QString totalSize;
    QString receivedSize;

    if (total < 1024)
        totalSize = tr ("%1 bytes").arg (total);

    else if (total < 1048576)
        totalSize = tr ("%1 KB").arg (round (total / 1024));

    else
        totalSize = tr ("%1 MB").arg (round (total / 1048576));

    if (received < 1024)
        receivedSize = tr ("%1 bytes").arg (received);

    else if (received < 1048576)
        receivedSize = tr ("%1 KB").arg (received / 1024);

    else
        receivedSize = tr ("%1 MB").arg (received / 1048576);

    m_ui->downloadLabel->setText (tr ("Downloading updates")
                                  + " (" + receivedSize + " " + tr ("of")
                                  + " " + totalSize + ")");
}

/**
 * Uses the \a received and \a total parameters to get the download progress
 * and update the progressbar value on the dialog.
 */
void Downloader::updateProgress (qint64 received, qint64 total)
{
    if (total > 0) {
        m_ui->progressBar->setMinimum (0);
        m_ui->progressBar->setMaximum (100);
        m_ui->progressBar->setValue ((received * 100) / total);

        calculateSizes (received, total);
        calculateTimeRemaining (received, total);
        saveFile (received, total);
    }

    else {
        m_ui->progressBar->setMinimum (0);
        m_ui->progressBar->setMaximum (0);
        m_ui->progressBar->setValue (-1);
        m_ui->downloadLabel->setText (tr ("Downloading Updates") + "...");
        m_ui->timeLabel->setText (QString ("%1: %2")
                                  .arg (tr ("Time Remaining"))
                                  .arg (tr ("Unknown")));
    }
}

/**
 * Uses two time samples (from the current time and a previous sample) to
 * calculate how many bytes have been downloaded.
 *
 * Then, this function proceeds to calculate the appropiate units of time
 * (hours, minutes or seconds) and constructs a user-friendly string, which
 * is displayed in the dialog.
 */
void Downloader::calculateTimeRemaining (qint64 received, qint64 total)
{
    uint difference = QDateTime::currentDateTime().toTime_t() - m_startTime;

    if (difference > 0) {
        QString timeString;
        qreal timeRemaining = total / (received / difference);

        if (timeRemaining > 7200) {
            timeRemaining /= 3600;
            int hours = int (timeRemaining + 0.5);

            if (hours > 1)
                timeString = tr ("about %1 hours").arg (hours);
            else
                timeString = tr ("about one hour");
        }

        else if (timeRemaining > 60) {
            timeRemaining /= 60;
            int minutes = int (timeRemaining + 0.5);

            if (minutes > 1)
                timeString = tr ("%1 minutes").arg (minutes);
            else
                timeString = tr ("1 minute");
        }

        else if (timeRemaining <= 60) {
            int seconds = int (timeRemaining + 0.5);

            if (seconds > 1)
                timeString = tr ("%1 seconds").arg (seconds);
            else
                timeString = tr ("1 second");
        }

        m_ui->timeLabel->setText (tr ("Time remaining") + ": " + timeString);
    }
}

/**
 * Rounds the given \a input to two decimal places
 */
qreal Downloader::round (const qreal& input)
{
    return roundf (input * 100) / 100;
}

QString Downloader::downloadDir() const
{
    return m_downloadDir.absolutePath();
}

void Downloader::setDownloadDir(const QString& downloadDir)
{
    if(m_downloadDir.absolutePath() != downloadDir) {
        m_downloadDir = downloadDir;
    }
}

/**
 * If the \a custom parameter is set to \c true, then the \c Downloader will not
 * attempt to open the downloaded file.
 *
 * Use the signals fired by the \c QSimpleUpdater to implement your own install
 * procedures.
 */
void Downloader::setUseCustomInstallProcedures (const bool custom)
{
    m_useCustomProcedures = custom;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1926673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI操作系统势头正猛,以后LINUX,和window,Android,IOS等等的OS都将被AI OS所取代!

AI操作系统是一种旨在利用人工智能技术来优化和管理计算资源的操作系统。它不仅仅是一个传统意义上的操作系统&#xff0c;而是一个能够自主学习和适应用户需求的智能平台。以下是对AI操作系统的一些关键特点和它是否能取代现有操作系统的讨论&#xff1a; AI操作系统的关键特…

OpenCV图像处理——判断轮廓是否在圆环内

要判断一个轮廓是否在圆环内&#xff0c;可以将问题分解为两个步骤&#xff1a; 确保轮廓的所有点都在外圆内。确保轮廓的所有点都在内圆外。 下面是一个完整的示例代码&#xff0c;展示如何实现这一点&#xff1a; #include <opencv2/opencv.hpp> #include <iostr…

QT VTK 简单测试工程

目录 1 目录结构 2 文件源码 3 运行结果 4 报错及处理 使用编译好的VTK库进行测试 1 目录结构 2 文件源码 Pro文件 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. #…

Iceberg概念和特性

1. 快照 Iceberg会随着时间的推进,跟踪表生命周期中的所有数据集变化,并使用快照(Snapshots)来表示每一次变化后的数据集合,每一次数据操作的事务提交均会产生一个快照,并将其记录在元数据文件(Metadata)中。 基于快照的概念,Iceberg有以下特性: 事务性:写入快照成…

数据链路层协议 ------------- 以太网协议

文章目录 链路层解决的问题![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ab01312d49ee479082a2255ad9fbfc99.png)以太网协议认识以太网以太网帧格式认识MAC地址对比理解MAC地址和IP地址认识MTUMUT对IP协议的影响MTU对UDP协议的影响MTU对TCP协议的影响ARP协议ARP协议…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(九)-无人机服务区分离

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

从课本上面开始学习的51单片机究竟有什么特点,在现在的市场上还有应用吗?

引言 51单片机&#xff0c;作为一种经典的微控制器&#xff0c;被广泛应用于各种嵌入式系统中。尽管如今ARM架构的高性能低成本单片机在市场上占据主导地位&#xff0c;但51单片机凭借其独特的优势依然在某些领域保持着应用价值。本文将深入探讨51单片机的特点、架构、应用以及…

读人工智能全传13人工智能导致的问题2

1. 机器人sha手 1.1. 自主57的话题总是带有强烈的煽动性&#xff0c;许多人会本能地厌恶它&#xff0c;认为这样的系统是不道德的&#xff0c;永远不该被建立 1.2. 自主57的讨论大多源于战争中使用得越来越频繁的无人机 1.3. 无人机 1.3.1. 人驾驶的飞机&#xff0c;在菌用…

MySQL空间索引

空间类型是建立在空间类型字段上的。 空间类型 MySQL的空间类型很多&#xff0c;我就不逐一介绍了。重要分四大类&#xff1a; GeometryCurveSurfaceGeometryCollection 前三种&#xff0c;地理、曲线、曲面都是不可实例化的。Geometry有一个子类Point, Curve有一个直接子类L…

ceph 部署

端口号 NFS 2049 rpcbind 111 NFS 目录越深&#xff0c;写入性能越差 操作简单&#xff0c; 一.前言&#xff1a;存储知识 1、存储基础 单机存储设备 【1】DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; IDE、SATA、SCSI、SAS…

springsecurity 身份认证

Spring Security简介 是一个功能强大且高度可定制的安全框架&#xff0c;它主要为Java程序提供声明式的身份验证&#xff08;认证&#xff09;和访问控制&#xff08;授权&#xff09;功能为基于Spring的企业应用系统提供了全面的安全解决方案&#xff0c;通过声明式的方式管理…

Windows中安装python/cmd中执行python命令无效

1、问题阐述? 本文章提供非常详细的安装教程。 本文章适合于不会安装python或者安装了python后,在cmd中执行python命令无效的情况。 2、下载python python下载官网地址:Download Python | Python.org 在下面的框子中选择你需要的版本 如果是windows选择如下包 如下版本…

基于Jetpack Compose实现的Android Preference

基于Jetpack Compose实现的Android Preference Jetpack Compose实现的Android偏好Preference实现,实现了CheckBoxPreference、EditTextPreference、SingleChoicePreference、SliderPreference等常见的 Preference 类型, 代码如下: 基类Preference :@Composable fun Prefer…

JMeter中进行JDBC协议压测

在使用Jmeter进行性能测试的时候,不仅是需要对业务的接口进行性能并发测试,有的时候还需要专门对服务器进行压测,比如本次给大家介绍的对数据库进行压测. 在JMeter中进行JDBC协议压测&#xff0c;你需要做以下几个步骤&#xff1a; 如何选择JDBC驱动程序 (1).掌握项目所采用…

泉盛UV-K5扩容2Mbit EEPROM

泉盛UV-K5扩容2Mbit EEPROM 步骤 分离前面板与背板。 拆下电池&#xff0c;底部有个空隙&#xff0c;从缝隙撬开背板。分离前面板时注意喇叭连接线&#xff0c;不要扯断了。 分离屏幕。 先从箭头位置向上挑起&#xff0c;屏幕稍微松动即可左右晃动&#xff0c;直至完全取出。注…

记一次docker容器安装MySQL,navicat无法连接报错(10060错误)

今天在云服务器上使用docker部署mysql 8.0.11时&#xff0c;遇到了一个诡异的问题&#xff0c;在云服务器的docker容器内可以连接上mysql&#xff0c;然而在自己电脑上连接mysql时报错&#xff1a;Can‘t connect to MySQL server on localhost (10060) 下面是网上搜寻的几种可…

知识图谱与LLMs:实时图分析(通过其关系的上下文理解数据点)

大型语言模型 (LLM) 极大地改变了普通人获取数据的方式。不到一年前&#xff0c;访问公司数据需要具备技术技能&#xff0c;包括熟练掌握各种仪表板工具&#xff0c;甚至深入研究数据库查询语言的复杂性。然而&#xff0c;随着 ChatGPT 等 LLM 的兴起&#xff0c;随着所谓的检索…

第17集《修习止观坐禅法要》

请大家打开讲义第四十二面&#xff0c;丁六、正修行第六。 这个是我们小止观正宗分的第六科&#xff0c;前面的五科是一个方便&#xff0c;是我们在修习止观的一个前方便&#xff0c;这一科是说明修习止观的正式的方法。 这个方便跟正修的关系&#xff0c;我们可以讲一个譬喻…

keepalive:

keepalive&#xff1a; 调度器的高可用 vip地址在主备之间的切换&#xff0c;主在工作时&#xff0c;vip地址只在主上&#xff0c;主停止工作&#xff0c;vip漂移到备服务器。 在主备的优先级不变的情况下&#xff0c;主恢复工作&#xff0c;vip会飘回到主服务器。 1、配优…

音视频开发—使用FFmpeg将YUV文件编码成H264裸流文件 C语言实现

文章目录 1.准备工作2.压缩编码工作流程3.详细步骤1. 初始化日志和参数检查2. 输入/输出文件的打开3. 查找和初始化编码器4. 打开编码器5. 帧内存的分配和初始化6. 设置转换上下文&#xff08;SWS&#xff09;7. 读取和转换数据8. 编码过程9. 资源清理 4.完整示例代码 1.准备工…