开源 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文件,同时打印下载进度。

文件下载的位置

相关推荐
Tipriest_6 小时前
C++ 中 ::(作用域解析运算符)的用途
开发语言·c++·作用域解析
Tipriest_7 小时前
求一个整数x的平方根到指定精度[C++][Python]
开发语言·c++·python
闻缺陷则喜何志丹8 小时前
【有序集合 有序映射 懒删除堆】 3510. 移除最小数对使数组有序 II|2608
c++·算法·力扣·有序集合·有序映射·懒删除堆
CoderJia程序员甲8 小时前
GitHub 热榜项目 - 日榜(2025-08-29)
ai·开源·github·开源项目·github热榜
云望无线图传模块8 小时前
突破视界的边界:16公里远距离无人机图传模块全面解析
网络·物联网·无人机
John_ToDebug9 小时前
从源码看浏览器弹窗消息机制:SetDefaultView 的创建、消息转发与本地/在线页通用实践
开发语言·c++·chrome
key_Go9 小时前
05.《ARP协议基础知识探秘》
运维·服务器·网络·华为·arp
励志不掉头发的内向程序员10 小时前
STL库——list(类模拟实现)
开发语言·c++·学习
好名字更能让你们记住我10 小时前
Linux网络基础1(一)之计算机网络背景
linux·服务器·网络·windows·计算机网络·算法·centos