QT 在线更新功能 申请管理员权限 Django服务器搭建

本文介绍了如何利用QT库中的网络请求功能和Django搭建的服务器,创建一个自动检查更新并解压zip文件的可执行程序,同时确保在更新过程中处理权限问题。
摘要由CSDN通过智能技术生成

 更新程序只需要替换部分需要更新的文件,不必整个程序重新安装。

主程序在运行的时候会一直占用需要的依赖文件。无法进行覆盖替换,所以当主程序将文件下载完后。启动另一个可执行程序解压下载的zip包,完成后在启动主程序。

所以需要编写另一个解压zip的可执行程序,zip是从服务器下载的更新文件,下载到主程序目录下将zip 解压后直接替换主程序的文件,有时候需要删除新增文件,也由这个程序完成。

  1. 检查是否需要更新
  2. 编写更新程序替换旧文件

解压zip 的可执行文件  需要在.por 文件中加入  QT +=  gui-private

 这里 char *argv[] 会接受主程序传过来的一些路径,这里的约束是

  1. 参数是主程序路径
  2. zip包的路径
  3. 解压后的目的地路径

Release编译后生成update.exe可执行文件, 解包后会无提示覆盖之前重复的文件。

#include <private/qzipreader_p.h>
#include <iostream>
#include <cstring>
#include <ShlObj_core.h>
int main(int argc, char *argv[]) {
    // 主程序路径
    const char *host = nullptr;
    // zip路径
    const char *zip = nullptr;
    // 解压路径
    const char *dest = nullptr;
    // -m 主程序完整路径
    // -z 下载的zip包完整路径
    // -d zip包解压的目的地
    
    // 这里自己约定参数,得到-m 后面的路径等
    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "-m") == 0) {
            host = argv[++i];
        } else if (strcmp(argv[i], "-z") == 0) {
            zip = argv[++i];
        } else if (strcmp(argv[i], "-d") == 0) {
            dest = argv[++i];
        }
    }

    QZipReader reader(zip);
    reader.extractAll(dest);
    reader.close();
    std::remove(zip);
    // 完成后启动主程序
    ShellExecuteA(nullptr, "open" , host, nullptr, nullptr, SW_SHOWNORMAL);
    return 0;
}

如何使用:

  1. 向服务器发起请求,查看服务器是否有新版本。
  2. 如果有新版本,下载新的版本需要的文件.zip,建议将文件下载到 Downloads下,避免权限
  3. 在运行update.exe之前检查是否有读写权限,如果没有就用管理员方式运行update.exe。
  4. 退出主程序运行update.exe解压zip覆盖之前的文件。

在检查是否有读写权限的时候,根据QT官翻:

出于性能原因,默认情况下,、 和相关类不会对 NTFS 文件系统执行完全所有权和权限 (ACL) 检查。在此类的任何实例的生存期内,将覆盖该默认值并执行高级检查。这提供了一种安全简便的方法来管理启用和禁用默认行为的此更改。

此类仅在 Windows 上可用。

qt_ntfs_permission_lookup

在 Qt 6.6 之前,用户必须直接操作全局变量。然而,这是一个非原子的全局变量,因此它容易出现数据争用。qt_ntfs_permission_lookup

因此,该变量从 Qt 6.6 开始被弃用。qt_ntfs_permission_lookup

需要用到QT 的网络请求模块在.pro中 QT+=network 如果是CMake  Qt::Network

#ifndef UPDATE_H
#define UPDATE_H


#define VERSION_MAJOR 1
#define VERSION_MINOR 1
#define VERSION_PATCH 0

#define VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
#define VERSION VERSION_CHECK(VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH)

#define MAJOR(version) ((version >> 16) & 0xFF)
#define MINOR(version)  ((version >> 8) & 0xFF)
#define PATCH(version) (version & 0xFF)

#include <ShlObj_core.h>

#pragma comment (lib, "Shell32.lib")

#include <Windows.h>
#include <QApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QFileInfo>
#include <QStandardPaths>
#include <QProcess>
#include <QPushButton>
#include <QObject>

class CheckVersion : public QObject {

public:
    explicit CheckVersion(QObject *parent = nullptr) : QObject(parent) {}

    void check() {
        QNetworkRequest request(QUrl("http://127.0.0.1:8080/update/"));
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        manager->post(request, QByteArray("可以向服务器反馈一些问题."));

        connect(manager, &QNetworkAccessManager::finished, this, [this](QNetworkReply *r) {
            if (r->error() == QNetworkReply::NoError) {
                QJsonParseError error;
                const QJsonDocument &document = QJsonDocument::fromJson(r->readAll(), &error);
                if (error.error == QJsonParseError::NoError) {
                    const QJsonObject &object = document.object();
                    int version = object["version"].toInt();
                    if (version > VERSION) {
                        const QJsonArray &content = object["content"].toArray();
                        for (int i = 0; i < content.size(); ++i) {
                            qDebug() << QString::number(i + 1) + ": " + content[i].toString();
                        }
                        qDebug() << QString("当前版本:%1.%2.%3   发现新版本:%4.%5.%6").arg(VERSION_MAJOR).arg(
                                VERSION_MINOR).arg(VERSION_PATCH).arg(MAJOR(version)).arg(MINOR(version)).arg(
                                PATCH(version));
                        const QString &zip = object["zip"].toString();
                        QNetworkReply *reply = manager->get(QNetworkRequest(zip));
                        // 准备下载路径
                        QFileInfo info(zip);
                        const QString &location =
                                QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" +
                                info.fileName();
                        file->setFileName(location);
                        file->open(QIODevice::WriteOnly);//打开文件
                        connect(reply, &QIODevice::readyRead, this, [this, reply] { file->write(reply->readAll()); });
                        connect(reply, &QNetworkReply::downloadProgress, [=](qint64 bytesRead, qint64 totalBytes) {
                            //下载进度
                            qreal progress = qreal(bytesRead) / qreal(totalBytes);
                            qDebug() << QString::number(progress * 100, 'f', 2) << "%    "
                                     << bytesRead / (1024 * 1024)
                                     << "MB" << "/" << totalBytes / (1024 * 1024) << "MB";
                        });

                        // 下载完成后 启动更新程序 解压下载的zip 并删除文件
                        QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, location] {
                            file->reset();// 重置
                            file->close();
                            if (reply->error() == QNetworkReply::NoError) {//启动更新程序
                                const QString &target = QCoreApplication::applicationDirPath();

                                bool writable;
                                QFileInfo info(target);
                                // 检查是否有写入权限
#if QT_VERSION > QT_VERSION_CHECK(6, 6, 0)
                                {
                                    QNtfsPermissionCheckGuard permissionGuard;
                                    writable = info.isWritable();
                                }
#else
                                qt_ntfs_permission_lookup++;
                                writable = info.isWritable();
                                qt_ntfs_permission_lookup--;
#endif

                                // 退出主程序
                                QApplication::exit(0);
                                // 此处是传递给更新程序的参数,注意:在传递路径的时候防止路径有空格特殊字符,需要加双引号。
                                // 在update.exe中得到约束
                                // -m 主程序完整路径
                                // -z 下载的zip包完整路径
                                // -d zip包解压的目的地
                                const std::string &args = QString(R"(-m "%1" -z "%2" -d "%3")").arg(
                                        QCoreApplication::applicationFilePath(), location, target).toStdString();
                                // 启动更新程序,如果没有写入权限就申请管理员方式运行
                                ShellExecuteA(nullptr,
                                              writable ? "open" : "runas",
                                              "update.exe",
                                              args.c_str(),
                                              nullptr, SW_SHOWNORMAL);
                            }
                            delete reply;
                        });
                    }
                }
            }
        });
    }


private:
    QNetworkAccessManager *manager{new QNetworkAccessManager(this)};
    QFile *file{new QFile()};
};

#endif // UPDATE_H
//  main.cpp

#include <QApplication>

#include "update.h"

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

    QPushButton button;
    auto*check = new CheckVersion(&button);

    button.resize(300,300);
    button.show();
    QObject::connect(&button,&QPushButton::clicked,check,&CheckVersion::check);


    return QApplication::exec();
}

 运行结果:

"1: 优化标题栏样式"
"2: 修复一些已知的BUG"
"3: 新增Application"
"当前版本:1.1.0   发现新版本:2.0.0"
"18.27" %     30 MB / 167 MB
"43.98" %     73 MB / 167 MB
"67.97" %     114 MB / 167 MB
"92.13" %     154 MB / 167 MB
"100.00" %     167 MB / 167 MB

Django 服务器搭建:

服务器的搭建一般会有专业人士搭建,这里用python Django 简单的搭建一个服务器。

Pycharm 新建一个Project

这里需要修改3个地方 ,打开 djangoProject-settings.py

注册:这里对应的就是url 的请求头,这里的update就是 "http://127.0.0.1:8080/update/"

如果是请求 plugin url就是 "http://127.0.0.1:8080/plugin /"

  views.update  views.plugin 是函数具体实现。

来到 app-views.py 实现函数。当发起对应的请求时会触发对应的函数。

然后 在终端 启动 django:

python manage.py runserver 0.0.0.0:8080

接下来用python 请求一下

import requests

result = requests.post("http://127.0.0.1:8080/update/").json()
print(result, "\n")
zip = result["zip"]
print(zip)

response = requests.get(zip)
with open("Testing.zip", "wb") as f:
    f.write(response.content)

成功请求: 

  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值