Qt多线程下载文件(包含进度条、异常处理)

Qt中的网络模块提供了高级的网络访问功能,但与WinInet API相比,它可能会在性能上略逊一筹。这可能是由于以下几个因素造成的:

  1. 抽象性和灵活性: Qt的网络模块提供了更高层次的抽象,允许开发者以更简洁的方式编写网络相关的代码。这种抽象性和灵活性可能会导致一些性能开销。

  2. 跨平台支持: Qt的网络模块是跨平台的,可以在多种操作系统上运行,包括Windows、macOS和Linux等。为了实现跨平台兼容性,可能会引入一些额外的抽象层,从而影响性能。

  3. 异步处理: Qt的网络模块通常使用异步方式进行网络访问,这意味着网络请求是在单独的线程中进行的,而不会阻塞主线程。虽然这样做提高了程序的响应性,但也可能会导致一些性能损失。

  4. 底层实现: Qt的网络模块可能使用不同的底层实现,如基于操作系统提供的网络库或者第三方库,这些底层实现的性能差异可能会影响到整体性能。

虽然Qt的网络模块在性能上可能略逊于WinInet API,但它提供了更高级的功能和更好的跨平台支持,适用于需要在不同操作系统上运行的应用程序。如果性能是关键考虑因素,可以考虑使用其他更专门的网络库或API来实现网络访问。

FileDownloaderThread.h

#ifndef FILEDOWNLOADER_H
#define FILEDOWNLOADER_H

#include <QThread>
#include <QDebug>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include <QEventLoop>
#include <QTimer>

class FileDownloader : public QThread
{
    Q_OBJECT

public:
    explicit FileDownloader(QObject *parent = nullptr);

    void setUrl(const QString &url, const QString &outputFile);

signals:
    void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    void downloadFinished(bool success, QString errorMessage);

protected:
    void run() override;

private:
    QString m_url;
    QString m_outputFile;
};

#endif // FILEDOWNLOADER_H

FileDownloaderThread.cpp

#include "FileDownloaderThread.h"

FileDownloader::FileDownloader(QObject *parent) : QThread(parent), m_url(""), m_outputFile("")
{
}

void FileDownloader::setUrl(const QString &url, const QString &outputFile)
{
    m_url = url;
    m_outputFile = outputFile;
}

void FileDownloader::run()
{
    // 如果URL或输出文件为空,则发出错误信号并返回
    if (m_url.isEmpty() || m_outputFile.isEmpty()) {
        emit downloadFinished(false, "url或保存路径有误");
        return;
    }

    QUrl                    qurl(m_url);
    QNetworkRequest         xrqt(qurl);
    QNetworkAccessManager   manager;
    QNetworkReply           *reply = NULL;
    QTimer                  timeoutTimer;
    QEventLoop              loop;

    // 如果网络请求失败,则发出错误信号并返回
    reply = manager.get(xrqt);
    if (!reply) {
        emit downloadFinished(false, "连接服务器失败.");
        return;
    }

    // 创建超时定时器
    timeoutTimer.setSingleShot(true);
    connect(&timeoutTimer, &QTimer::timeout, [&](){
        // 如果超时,则中止下载
        reply->abort();
    });

    // 连接下载进度信号
    connect(reply, &QNetworkReply::downloadProgress, [&](qint64 bytesReceived, qint64 bytesTotal){
        emit downloadProgress(bytesReceived, bytesTotal);
        // 更新超时计时器
        timeoutTimer.start(10000); // 重启超时定时器
    });

    // 连接下载完成信号
    connect(reply, &QNetworkReply::finished, [&](){
        // 停止超时计时器
        timeoutTimer.stop();

        // 根据网络响应的状态进行处理
        if (reply->error() == QNetworkReply::NoError)
        {
            QFile   file(m_outputFile);
            if (file.open(QIODevice::WriteOnly))
            {
                file.write(reply->readAll());
                file.close();
                emit downloadFinished(true, "下载成功.");
            }
            else
            {
                emit downloadFinished(false, "下载完毕,但写入文件失败.");
            }
        }
        else
        {
            emit downloadFinished(false, reply->errorString());
        }

        // 删除网络回复对象
        reply->deleteLater();
    });

    // 启动超时定时器
    timeoutTimer.start(10000); // 设置初始超时时间为10秒

    // 开启事件循环
    connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    // 如果超时计时器仍在运行,则说明下载超时
    if (timeoutTimer.isActive())
    {
        timeoutTimer.stop(); // 停止超时计时器
        emit downloadFinished(false, "下载超时.");
    }
}

调用测试方法:

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void startDownload();
    void updateProgress(qint64 bytesReceived, qint64 bytesTotal);
    void handleDownloadFinished(bool success, QString errorMessage);

    void on_btnDown_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "FileDownloaderThread.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::startDownload() {
    FileDownloader *downloader = new FileDownloader(this);
    downloader->setUrl("http://xxx.com/xxx.png", "xxx.png");
    connect(downloader, &FileDownloader::downloadProgress, this, &MainWindow::updateProgress);
    connect(downloader, &FileDownloader::downloadFinished, this, &MainWindow::handleDownloadFinished);
    downloader->start();
}

void MainWindow::updateProgress(qint64 bytesReceived, qint64 bytesTotal) {
    if (bytesTotal > 0) {
        ui->progressBar->setValue(100 * bytesReceived / bytesTotal);
    }
}

void MainWindow::handleDownloadFinished(bool success, QString errorMessage) {
    if (success) {
        qDebug() << "Download successful!";
    } else {
        qDebug() << "Download failed: " << errorMessage;
    }

    ui->btnDoiwn->setEnabled(true);
}

void MainWindow::on_btnDown_clicked()
{
    ui->btnDoiwn->setEnabled(false);
    startDownload();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪宁宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值