开源 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博客
推荐链接:
开源 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中的文件名)
✅ 自动处理重定向
✅ 错误处理和资源清理
✅ 进度实时反馈
4. 使用示例
// 创建下载器
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 += widgetsTARGET = 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.0SOURCES += \main.cpp \widget.cpp \downloadtool.cppHEADERS += \widget.h \downloadtool.hFORMS += \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; // 保存构造时传入的下载urlQString 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_DEBUGqDebug() << QString("Invalid URL: %1: %2").arg(m_downloadUrl, newUrl.errorString());
#endif // DOWNLOAD_DEBUGreturn;}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_DEBUGqDebug() << QString("Download failed: %1.").arg(reply->errorString());
#endif // DOWNLOAD_DEBUGreturn;}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_DEBUGqDebug() << 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_DEBUGqDebug() << 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_DEBUGqDebug() << 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_DEBUGqDebug() << QString("Unable to save the file %1: %2.").arg(QDir::toNativeSeparators(fileName), file->errorString());
#endif // DOWNLOAD_DEBUGreturn nullptr;}return file;
}
3.4 widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic: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文件,同时打印下载进度。
文件下载的位置