基于Qt HTTP应用程序项目案例

# 主项目入口

main函数创建对象空间,确认窗口的大小和坐标。


#include 
#include 
#include   // 
#include 
#include "httpwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    HttpWindow httpWin;  // QDialog的子类
    const QRect availableSize = httpWin.screen()->availableGeometry();
    qDebug()<<"availableSize = "<

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目视频教程+代码,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

# 项目子头文件httpwindow.h

需要的头文件:


QT_BEGIN_NAMESPACE
class QFile;    // 文件
class QLabel;   // 标签
class QLineEdit;    // 行编辑
class QPushButton;  // 按钮
class QSslError;    //  QSslError类提供SSL错误
class QAuthenticator;   // QAuthenticator类提供了一个身份验证对象。
class QNetworkReply;  // 网络应答
class QCheckBox;  // 检查盒子
QT_END_NAMESPACE

根据给出的类名,以下是各个类的作用的简要介绍:

1. QFile(文件):QFile类提供了对文件的读写操作。它可以用于打开、关闭、读取和写入文件,并提供了一些用于文件操作的便捷方法。

2. QLabel(标签):QLabel类用于显示文本或图像。它通常用于在用户界面中显示静态文本或图像,并且不允许用户进行编辑操作。

3. QLineEdit(行编辑):QLineEdit类提供了一个单行文本编辑框,用于接收用户输入的文本。它可以用于接收单行的用户输入,比如用户名、密码等。

4. QPushButton(按钮):QPushButton类是一个按钮部件,用于触发某些操作或执行特定的功能。当用户单击按钮时,可以连接按钮的信号(clicked)到槽函数,从而实现相关的操作。

5. QSslError(SSL错误):QSslError类提供了有关SSL连接中发生的错误的信息。它用于处理与SSL(Secure Sockets Layer 安全套接字层)相关的错误,如证书验证失败、主机名不匹配等。

6. QAuthenticator(身份验证对象):QAuthenticator类提供了一个身份验证对象,用于在进行网络请求时进行身份验证。它可以用于管理用户名和密码等身份验证信息,并与网络请求一起使用。

7. QNetworkReply:QNetworkReply类用于表示网络请求的响应。它提供了访问响应数据、处理错误和其他网络相关操作的方法。

8. QCheckBox:QCheckBox类是一个复选框部件,用于允许用户选择多个选项。它可以用于表示二进制状态,比如打开/关闭、选中/未选中等。


class ProgressDialog : public QProgressDialog {
    Q_OBJECT

public:
    explicit ProgressDialog(const QUrl &url, QWidget *parent = nullptr);
    ~ProgressDialog();

public slots:
   void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);
};

代码解释:

ProgressDialog类的作用是显示一个进度对话框,用于显示网络请求的进度。它通常用于在进行网络请求时显示一个进度条,以便用户可以看到请求的进展情况。

构造函数ProgressDialog(const QUrl &url, QWidget *parent = nullptr)接受一个QUrl类型的参数和一个可选的父部件参数。它用于创建一个ProgressDialog对象,并指定要进行网络请求的URL。

析构函数~ProgressDialog()用于在ProgressDialog对象被销毁时进行清理工作。

networkReplyProgress(qint64 bytesRead, qint64 totalBytes)是一个公共槽函数(public slot),用于处理网络请求的进度更新。它接受两个参数:bytesRead表示已读取的字节数,totalBytes表示总字节数。该槽函数可以在网络请求的过程中更新进度对话框的进度条。

通过继承自QProgressDialog类,ProgressDialog类可以使用QProgressDialog类提供的功能,如设置进度条的范围、设置进度文本等。


class HttpWindow : public QDialog
{
    Q_OBJECT

public:
    explicit HttpWindow(QWidget *parent = nullptr);
    ~HttpWindow();

    void startRequest(const QUrl &requestedUrl);

private slots:
    void downloadFile();
    void cancelDownload();
    void httpFinished();
    void httpReadyRead();
    void enableDownloadButton();
    void slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator);
#ifndef QT_NO_SSL
    void sslErrors(QNetworkReply *, const QList &errors);
#endif

private:
    std::unique_ptr openFileForWrite(const QString &fileName);

    QLabel *statusLabel;
    QLineEdit *urlLineEdit;
    QPushButton *downloadButton;
    QCheckBox *launchCheckBox;
    QLineEdit *defaultFileLineEdit;
    QLineEdit *downloadDirectoryLineEdit;

    QUrl url;
    QNetworkAccessManager qnam;
    QNetworkReply *reply;
    std::unique_ptr file;
    bool httpRequestAborted; // http请求已中止
};

代码解释:

HttpWindow类的作用是提供一个界面,用于进行HTTP请求和文件下载操作。它包含了一些用于处理HTTP请求和文件下载的槽函数,并使用了Qt的网络模块(QNetworkAccessManager和QNetworkReply)进行网络通信。

构造函数`HttpWindow(QWidget *parent = nullptr)`接受一个可选的父部件参数,并用于创建一个HttpWindow对象。析构函数`~HttpWindow()`用于在HttpWindow对象被销毁时进行清理工作。

`startRequest(const QUrl &requestedUrl)`函数用于开始进行HTTP请求。它接受一个QUrl类型的参数,表示要请求的URL。该函数会初始化网络请求,并发送HTTP请求到指定的URL。

槽函数`downloadFile()`、`cancelDownload()`、`httpFinished()`、`httpReadyRead()`、`enableDownloadButton()`、`slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator)`和`sslErrors(QNetworkReply *, const QList &errors)`分别用于处理下载文件、取消下载、HTTP请求完成、HTTP请求可读、启用下载按钮、处理身份验证和处理SSL错误等操作。

`openFileForWrite(const QString &fileName)`函数是一个私有函数,用于打开一个文件以供写入。它接受一个文件名参数,并返回一个指向QFile的唯一指针,用于写入文件。

HttpWindow类还包含了一些成员变量,如statusLabel(标签部件,用于显示状态信息)、urlLineEdit(行编辑部件,用于输入URL)、downloadButton(按钮部件,用于触发文件下载)、launchCheckBox(复选框部件,用于选择是否启动下载后的文件)、defaultFileLineEdit(行编辑部件,用于指定默认的下载文件名)和downloadDirectoryLineEdit(行编辑部件,用于指定下载文件的目录)等。


## httpwindow.h


#ifndef HTTPWINDOW_H
#define HTTPWINDOW_H

#include 
#include 
#include 

#include 

QT_BEGIN_NAMESPACE
class QFile;    // 文件
class QLabel;   // 标签
class QLineEdit;    // 行编辑
class QPushButton;  // 按钮
class QSslError;    //  QSslError类提供SSL错误
class QAuthenticator;   // QAuthenticator类提供了一个身份验证对象。
class QNetworkReply;
class QCheckBox;

QT_END_NAMESPACE

class ProgressDialog : public QProgressDialog {
    Q_OBJECT

public:
    explicit ProgressDialog(const QUrl &url, QWidget *parent = nullptr);
    ~ProgressDialog();

public slots:
   void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);
};

class HttpWindow : public QDialog
{
    Q_OBJECT

public:
    explicit HttpWindow(QWidget *parent = nullptr);
    ~HttpWindow();

    void startRequest(const QUrl &requestedUrl);

private slots:
    void downloadFile();
    void cancelDownload();
    void httpFinished();
    void httpReadyRead();
    void enableDownloadButton();
    void slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator);
#ifndef QT_NO_SSL
    void sslErrors(QNetworkReply *, const QList &errors);
#endif

private:
    std::unique_ptr openFileForWrite(const QString &fileName);

    QLabel *statusLabel;
    QLineEdit *urlLineEdit;
    QPushButton *downloadButton;
    QCheckBox *launchCheckBox;
    QLineEdit *defaultFileLineEdit;
    QLineEdit *downloadDirectoryLineEdit;

    QUrl url;
    QNetworkAccessManager qnam;
    QNetworkReply *reply;
    std::unique_ptr file;
    bool httpRequestAborted;
};

#endif

## 源文件httpwindow.cpp


#include "httpwindow.h"

#include "ui_authenticationdialog.h"

#include 
#include 
#include 

#if QT_CONFIG(ssl)  // #if QT_CONFIG(ssl) 的目的是检查 Qt 库是否已配置 SSL 模块
const char defaultUrl[] = "https://www.baidu.com/";
#else
const char defaultUrl[] = "https://www.baidu.com/";
#endif
const char defaultFileName[] = "index.html";    // 默认文件名

ProgressDialog::ProgressDialog(const QUrl &url, QWidget *parent)
  : QProgressDialog(parent)
{
    this->setWindowTitle(tr("Download Progress"));
    this->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);   // 移除 WindowContextHelpButtonHint。WindowContextHelpButtonHint 是一个标志,用于表示窗口是否应显示帮助按钮
    this->setLabelText(tr("Downloading %1.").arg(url.toDisplayString()));   // 显示正在下载的文件的名称
    this->setMinimum(0);
    this->setValue(0);
    this->setMinimumDuration(0);
    this->setMinimumSize(QSize(400, 75)); // 最小窗口
}

ProgressDialog::~ProgressDialog()
{
}

void ProgressDialog::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{
    setMaximum(totalBytes);
    setValue(bytesRead);
}

HttpWindow::HttpWindow(QWidget *parent)
    : QDialog(parent)
    , statusLabel(new QLabel(tr("Please enter the URL of a file you want to download.\n\n"), this))
    , urlLineEdit(new QLineEdit(defaultUrl))
    , downloadButton(new QPushButton(tr("Download")))
    , launchCheckBox(new QCheckBox("Launch file"))
    , defaultFileLineEdit(new QLineEdit(defaultFileName))
    , downloadDirectoryLineEdit(new QLineEdit)
    , reply(nullptr)
    , file(nullptr)
    , httpRequestAborted(false)
{
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
    setWindowTitle(tr("HTTP"));

    connect(&qnam, &QNetworkAccessManager::authenticationRequired,
            this, &HttpWindow::slotAuthenticationRequired);
#ifndef QT_NO_SSL
    connect(&qnam, &QNetworkAccessManager::sslErrors,
            this, &HttpWindow::sslErrors);
#endif

    QFormLayout *formLayout = new QFormLayout;
    urlLineEdit->setClearButtonEnabled(true);
    connect(urlLineEdit, &QLineEdit::textChanged,
            this, &HttpWindow::enableDownloadButton);
    formLayout->addRow(tr("&URL:"), urlLineEdit);
    QString downloadDirectory = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
    if (downloadDirectory.isEmpty() || !QFileInfo(downloadDirectory).isDir())
        downloadDirectory = QDir::currentPath();
    downloadDirectoryLineEdit->setText(QDir::toNativeSeparators(downloadDirectory));
    formLayout->addRow(tr("&Download directory:"), downloadDirectoryLineEdit);
    formLayout->addRow(tr("Default &file:"), defaultFileLineEdit);
    launchCheckBox->setChecked(true);
    formLayout->addRow(launchCheckBox);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addLayout(formLayout);

    mainLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));

    statusLabel->setWordWrap(true);
    mainLayout->addWidget(statusLabel);

    downloadButton->setDefault(true);
    connect(downloadButton, &QAbstractButton::clicked, this, &HttpWindow::downloadFile);
    QPushButton *quitButton = new QPushButton(tr("Quit"));
    quitButton->setAutoDefault(false);
    connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
    QDialogButtonBox *buttonBox = new QDialogButtonBox;
    buttonBox->addButton(downloadButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
    mainLayout->addWidget(buttonBox);

    urlLineEdit->setFocus();
}

HttpWindow::~HttpWindow()
{
}

void HttpWindow::startRequest(const QUrl &requestedUrl)
{
    url = requestedUrl;
    httpRequestAborted = false;

    reply = qnam.get(QNetworkRequest(url));
    connect(reply, &QNetworkReply::finished, this, &HttpWindow::httpFinished);
    connect(reply, &QIODevice::readyRead, this, &HttpWindow::httpReadyRead);

    ProgressDialog *progressDialog = new ProgressDialog(url, this);
    progressDialog->setAttribute(Qt::WA_DeleteOnClose);
    connect(progressDialog, &QProgressDialog::canceled, this, &HttpWindow::cancelDownload);
    connect(reply, &QNetworkReply::downloadProgress, progressDialog, &ProgressDialog::networkReplyProgress);
    connect(reply, &QNetworkReply::finished, progressDialog, &ProgressDialog::hide);
    progressDialog->show();

    statusLabel->setText(tr("Downloading %1...").arg(url.toString()));
}

void HttpWindow::downloadFile()
{
    const QString urlSpec = urlLineEdit->text().trimmed();
    if (urlSpec.isEmpty())
        return;

    const QUrl newUrl = QUrl::fromUserInput(urlSpec);
    if (!newUrl.isValid()) {
        QMessageBox::information(this, tr("Error"),
                                 tr("Invalid URL: %1: %2").arg(urlSpec, newUrl.errorString()));
        return;
    }

    QString fileName = newUrl.fileName();
    if (fileName.isEmpty())
        fileName = defaultFileLineEdit->text().trimmed();
    if (fileName.isEmpty())
        fileName = defaultFileName;
    QString downloadDirectory = QDir::cleanPath(downloadDirectoryLineEdit->text().trimmed());
    bool useDirectory = !downloadDirectory.isEmpty() && QFileInfo(downloadDirectory).isDir();
    if (useDirectory)
        fileName.prepend(downloadDirectory + '/');
    if (QFile::exists(fileName)) {
        if (QMessageBox::question(this, tr("Overwrite Existing File"),
                                  tr("There already exists a file called %1%2."
                                     " Overwrite?")
                                     .arg(fileName,
                                          useDirectory
                                           ? QString()
                                           : QStringLiteral(" in the current directory")),
                                     QMessageBox::Yes | QMessageBox::No,
                                     QMessageBox::No)
            == QMessageBox::No) {
            return;
        }
        QFile::remove(fileName);
    }

    file = openFileForWrite(fileName);
    if (!file)
        return;

    downloadButton->setEnabled(false);

    // schedule the request
    startRequest(newUrl);
}

std::unique_ptr HttpWindow::openFileForWrite(const QString &fileName)
{
    std::unique_ptr file(new QFile(fileName));
    if (!file->open(QIODevice::WriteOnly)) {
        QMessageBox::information(this, tr("Error"),
                                 tr("Unable to save the file %1: %2.")
                                 .arg(QDir::toNativeSeparators(fileName),
                                      file->errorString()));
        return nullptr;
    }
    return file;
}

void HttpWindow::cancelDownload()
{
    statusLabel->setText(tr("Download canceled."));
    httpRequestAborted = true;
    reply->abort();
    downloadButton->setEnabled(true);
}

void HttpWindow::httpFinished()
{
    QFileInfo fi;
    if (file) {
        fi.setFile(file->fileName());
        file->close();
        file.reset();
    }

    if (httpRequestAborted) {
        reply->deleteLater();
        reply = nullptr;
        return;
    }

    if (reply->error()) {
        QFile::remove(fi.absoluteFilePath());
        statusLabel->setText(tr("Download failed:\n%1.").arg(reply->errorString()));
        downloadButton->setEnabled(true);
        reply->deleteLater();
        reply = nullptr;
        return;
    }

    const QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);

    reply->deleteLater();
    reply = nullptr;

    if (!redirectionTarget.isNull()) {
        const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
        if (QMessageBox::question(this, tr("Redirect"),
                                  tr("Redirect to %1 ?").arg(redirectedUrl.toString()),
                                  QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
            QFile::remove(fi.absoluteFilePath());
            downloadButton->setEnabled(true);
            statusLabel->setText(tr("Download failed:\nRedirect rejected."));
            return;
        }
        file = openFileForWrite(fi.absoluteFilePath());
        if (!file) {
            downloadButton->setEnabled(true);
            return;
        }
        startRequest(redirectedUrl);
        return;
    }

    statusLabel->setText(tr("Downloaded %1 bytes to %2\nin\n%3")
                         .arg(fi.size()).arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath())));
    if (launchCheckBox->isChecked())
        QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
    downloadButton->setEnabled(true);
}

void HttpWindow::httpReadyRead()
{
    //每当QNetworkReply有新数据时,就会调用此插槽。
    //我们读取它的所有新数据并将其写入文件。
    //这样,我们使用的RAM比在finished()时读取时要少
    //QNetworkReply的信号
    if (file)
        file->write(reply->readAll());
}

void HttpWindow::enableDownloadButton()
{
    downloadButton->setEnabled(!urlLineEdit->text().isEmpty());
}

void HttpWindow::slotAuthenticationRequired(QNetworkReply *, QAuthenticator *authenticator)
{
    QDialog authenticationDialog;
    Ui::Dialog ui;
    ui.setupUi(&authenticationDialog);
    authenticationDialog.adjustSize();
    ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm(), url.host()));

    //URL是否包含信息?填充UI
    //只有当URL提供的凭据错误时,这才相关
    ui.userEdit->setText(url.userName());
    ui.passwordEdit->setText(url.password());

    if (authenticationDialog.exec() == QDialog::Accepted) {
        authenticator->setUser(ui.userEdit->text());
        authenticator->setPassword(ui.passwordEdit->text());
    }
}

#ifndef QT_NO_SSL
void HttpWindow::sslErrors(QNetworkReply *, const QList &errors)
{
    QString errorString;
    for (const QSslError &error : errors) {
        if (!errorString.isEmpty())
            errorString += '\n';
        errorString += error.errorString();
    }

    if (QMessageBox::warning(this, tr("SSL Errors"),
                             tr("One or more SSL errors has occurred:\n%1").arg(errorString),
                             QMessageBox::Ignore | QMessageBox::Abort) == QMessageBox::Ignore) {
        reply->ignoreSslErrors();
    }
}
#endif

## ui文件

基于Qt HTTP应用程序项目案例_第1张图片

效果演示

基于Qt HTTP应用程序项目案例_第2张图片

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(Qt实战项目视频教程+代码,C++语言基础,C++设计模式,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓ 

你可能感兴趣的:(QT开发,qt,http,C++项目,qt开发,qt教程,qt编程,C/C++)