开源 C++ QT Widget 开发(八)网络--Http文件下载

文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客

开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客

开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客

开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客

开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客

开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客

开源 C++ QT Widget 开发(七)线程--多线程及通讯-CSDN博客

开源 C++ QT Widget 开发(八)网络--Http文件下载-CSDN博客

开源 C++ QT Widget 开发(九)图表--仪表盘-CSDN博客

推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

本章主要内容:基于 Qt Http 的网络文件下载器。

目录:

1.主要内容

2.核心源码分析

3.所有源码

4.显示效果

一、主要内容

使用 QNetworkAccessManager 来处理 HTTP 请求,实现了文件的下载、进度显示、取消下载以及重定向处理等功能。DownloadTool 类封装了所有下载相关的核心逻辑,而 Widget 类则提供了一个简单的界面来触发下载操作。

二、核心源码分析

2.1 头文件部分

继承关系:继承自QObject,支持Qt的信号槽机制

网络组件:使用QNetworkAccessManager和QNetworkReply处理网络请求

内存管理:使用std::unique_ptr智能指针管理文件对象

线程安全:使用QPointer防止悬挂指针问题

2.2 主要功能

// 构造函数:接收下载URL和保存路径

DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent = nullptr);

// 开始下载

void startDownload();

// 取消下载

void cancelDownload();

// 信号:进度更新和完成通知

void sigProgress(qint64 bytesRead, qint64 totalBytes, qreal progress);

void sigDownloadFinished();

2.3 实现细节分析

下载流程:

startDownload() 验证URL并准备文件路径

startRequest() 发起网络请求并连接信号槽

httpReadyRead() 实时写入接收到的数据

networkReplyProgress() 更新下载进度

httpFinished() 处理下载完成或错误

关键特性:

✅ 自动创建保存目录

✅ 支持文件重命名(使用URL中的文件名)

✅ 自动处理重定向

✅ 错误处理和资源清理

✅ 进度实时反馈

  1. 使用示例

    // 创建下载器
    DownloadTool* downloader = new DownloadTool(
    "http://example.com/file.zip",
    "/path/to/save"
    );

    // 连接信号
    connect(downloader, &DownloadTool::sigProgress, [](qint64 bytes, qint64 total, qreal progress) {
    qDebug() << "Progress:" << progress * 100 << "%";
    });

    connect(downloader, &DownloadTool::sigDownloadFinished, {
    qDebug() << "Download finished!";
    });

    // 开始下载
    downloader->startDownload();

三、所有源码

3.1 .pro文件

复制代码
d#-------------------------------------------------
#
# Project created by QtCreator 2024-01-19T15:33:43
#
#-------------------------------------------------

QT       += core gui
QT       += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = DownloadTool
TEMPLATE = app

# The following define makes your csssompiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
        main.cpp \
        widget.cpp \
    downloadtool.cpp

HEADERS += \
        widget.h \
    downloadtool.h

FORMS += \
        widget.ui

3.2 downloadTool.h

复制代码
#ifndef DOWNLOADTOOL_H
#define DOWNLOADTOOL_H
#pragma once

#include <QObject>        // QObject类是Qt对象模型的核心
#include <QUrl>           // QUrl类提供了使用URL的便捷接口
#include <QFile>          // QFile类用于对文件进行读写操作
#include <QDir>           // QDir类用于操作路径名及底层文件系统
#include <QPointer>       // QPointer指针引用的对象被销毁时候,会自动指向NULL,解决指针悬挂问题
#include <QApplication>   // 此处用于获取当前程序绝对路径
#include <QtNetwork/QNetworkReply>

#include <QtNetwork/QNetworkAccessManager>  // QNetworkAccessManager类为应用提供发送网络请求和接收答复的API接口
#include <memory>         // 使用std::unique_ptr需要包含该头文件

#define DOWNLOAD_DEBUG    // 是否打印输出

class DownloadTool : public QObject  // 继承QObject
{
    Q_OBJECT              // 加入此宏,才能使用QT中的signal和slot机制

public:
    // 构造函数参数:  1)http文件完整的url  2)保存的路径
    explicit DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent = nullptr);
    ~DownloadTool();

    void startDownload();  // 开始下载文件
    void cancelDownload(); // 取消下载文件

Q_SIGNALS:
    void sigProgress(qint64 bytesRead, qint64 totalBytes, qreal progress);  // 下载进度信号
    void sigDownloadFinished();  // 下载完成信号

private Q_SLOTS:
    void httpFinished();    // QNetworkReply::finished对应的槽函数
    void httpReadyRead();   // QIODevice::readyRead对应的槽函数

    void networkReplyProgress(qint64 bytesRead, qint64 totalBytes);  // QNetworkReply::downloadProgress对应的槽函数

private:
    void startRequest(const QUrl& requestedUrl);
    std::unique_ptr<QFile> openFileForWrite(const QString& fileName);

private:
    QString m_downloadUrl;  // 保存构造时传入的下载url
    QString m_savePath;     // 保存构造时传入的保存路径

    const QString defaultFileName = "tmp";  // 默认下载到tmp文件夹

    QUrl url;
    QNetworkAccessManager qnam;
    QPointer<QNetworkReply> reply;
    std::unique_ptr<QFile> file;
    bool httpRequestAborted;
};

#endif // DOWNLOADTOOL_H

3.3 downloadTool.cpp

复制代码
#include "DownloadTool.h"

DownloadTool::DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent)
    : QObject(parent)
{
    m_downloadUrl = downloadUrl;
    m_savePath    = savePath;
}

DownloadTool::~DownloadTool() {}

void DownloadTool::startDownload()
{
    const QUrl newUrl = QUrl::fromUserInput(m_downloadUrl);

    if (!newUrl.isValid()) {
#ifdef DOWNLOAD_DEBUG
        qDebug() << QString("Invalid URL: %1: %2").arg(m_downloadUrl, newUrl.errorString());
#endif // DOWNLOAD_DEBUG
        return;
    }

    QString fileName = newUrl.fileName();

    if (fileName.isEmpty()) fileName = defaultFileName;
    if (m_savePath.isEmpty()) { m_savePath = QApplication::applicationDirPath() + "/tmp"; }
    if (!QFileInfo(m_savePath).isDir()) {
        QDir dir;
        dir.mkpath(m_savePath);
    }

    fileName.prepend(m_savePath + '/');
    if (QFile::exists(fileName)) { QFile::remove(fileName); }
    file = openFileForWrite(fileName);
    if (!file) return;

    startRequest(newUrl);
}

void DownloadTool::cancelDownload()
{
    httpRequestAborted = true;
    reply->abort();
}

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

    if (httpRequestAborted) {
        return;
    }

    if (reply->error()) {
        QFile::remove(fi.absoluteFilePath());
#ifdef DOWNLOAD_DEBUG
        qDebug() << QString("Download failed: %1.").arg(reply->errorString());
#endif // DOWNLOAD_DEBUG
        return;
    }

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

    if (!redirectionTarget.isNull()) {
        const QUrl redirectedUrl = url.resolved(redirectionTarget.toUrl());
        file = openFileForWrite(fi.absoluteFilePath());
        if (!file) { return; }
        startRequest(redirectedUrl);
        return;
    }

    Q_EMIT sigDownloadFinished();

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString(tr("Downloaded %1 bytes to %2 in %3")
        .arg(fi.size()).arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath())));
    qDebug() << "Finished";
#endif // DOWNLOAD_DEBUG
}

void DownloadTool::httpReadyRead()
{
    if (file) file->write(reply->readAll());
}

void DownloadTool::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{
    qreal progress = qreal(bytesRead) / qreal(totalBytes);
    Q_EMIT sigProgress(bytesRead, totalBytes, progress);

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString::number(progress * 100, 'f', 2) << "%    "
        << bytesRead / (1024 * 1024) << "MB" << "/" << totalBytes / (1024 * 1024) << "MB";
#endif // DOWNLOAD_DEBUG
}

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

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

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString(tr("Downloading %1...").arg(url.toString()));
#endif // DOWNLOAD_DEBUG
}

std::unique_ptr<QFile> DownloadTool::openFileForWrite(const QString& fileName)
{
    std::unique_ptr<QFile> file(new QFile(fileName));
    if (!file->open(QIODevice::WriteOnly)) {
#ifdef DOWNLOAD_DEBUG
        qDebug() << QString("Unable to save the file %1: %2.")
            .arg(QDir::toNativeSeparators(fileName), file->errorString());
#endif // DOWNLOAD_DEBUG
        return nullptr;
    }
    return file;
}

3.4 widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_btnDown_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

3.5 widget.cpp文件

复制代码
#include "widget.h"
#include "ui_widget.h"
#include "DownloadTool.h"
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

}

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

void Widget::on_btnDown_clicked()
{
    DownloadTool* dT;
    //dT = new DownloadTool("https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe", QApplication::applicationDirPath() + "/download");
    dT = new DownloadTool("https://dl.360safe.com/pclianmeng/n/3__3112523__3f7372633d6c6d266c733d6e33366163663466393961__68616f2e3336302e636e__0cd2.exe", QApplication::applicationDirPath() + "/download");
    dT->startDownload();
}

3.6 main.cpp

复制代码
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

四、显示效果

点击下载,下载360卫士的exe文件,同时打印下载进度。

文件下载的位置

相关推荐
打不了嗝 ᥬ᭄14 小时前
【Linux】网络基础
linux·运维·网络
青草地溪水旁14 小时前
设计模式(C++)详解——命令模式(1)
c++·设计模式·命令模式
青草地溪水旁14 小时前
设计模式(C++)详解——命令模式(2)
c++·设计模式·命令模式
敲上瘾14 小时前
HTTP协议工作原理与生产环境服务器搭建实战
服务器·网络·c++·网络协议·http
Zfox_15 小时前
【C++项目】微服务即时通讯系统:服务端
数据库·c++·微服务·中间件·rpc·架构·即时通讯
风车带走过往15 小时前
部署开源PPTagent 生成工具
linux·开源
清朝牢弟15 小时前
基于Win系统下PCL库入门到实践:IO模块之PCD文件的读写(附详细代码)
c++·pcl·pcd
爱和冰阔落15 小时前
【C++STL详解】带头双向循环结构 + 双向迭代器,核心接口 + 排序效率 + 避坑指南
开发语言·c++·经验分享
今天也好累15 小时前
贪心算法之分数背包问题
c++·笔记·学习·算法·贪心算法
carver w15 小时前
c++,数据结构,unordermap哈希表基本操作
数据结构·c++·散列表