Web网络服务模块——下载升级文件

本系列笔记所记述的books项目设计并实现了程序自动升级功能,因此需要从网络上下载升级EXE文件,这涉及网络下载的相关功能。

信号和槽函数

针对网络方面的编程,Qt中只用几行代码就可以实现其他语言程序需要大量代码才能实现的功能,由此可见Qt在网络通信方面的优势。网络通信中重要且复杂的消息处理相关代码在Qt程序中仅用短短2行即可完成(connect函数),这2行代码实现了Qt的消息传递机制,即信号与槽函数。

本节中我仅针对与网络通信相关的3个比较常用的信号与槽关联函数进行讨论,代码如下:

connect(pReply, QNetworkReply::finished, this, myFinished);
connect(pReply, QNetworkReply::downloadProgress, this, myDownloadProgress);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, 
        SLOT(slotError(QNetworkReply::NetworkError)));
  1. 第1行代码表示当网络通信任务完成(如下载完成)时发出QNetworkReply::finished信号,connect函数将这个信号与自定义的myFinished函数相关联,由myFinished函数处理通信任务完成后程序应该执行的功能(如将下载的数据保存到文件中)。
  2. 第2行代码处理的是下载进度,由自定义的myDownloadProgress实现。为保证用户友好性,在下载较大文件时,需要为用户提供下载进度。QNetworkReply::downloadProgress发出信号时,会传递出“当前传输数据量”和“应传输数据总量”。自定义的myDownloadProgress函数通过qint64 bytesReceivedqint64 bytesTotal参数向用户传递当前传输数据量和应传输数据总量。QNetworkReply::downloadProgress并不实时发出信号,而是在系统较空闲的情况下发送,myDownloadProgress函数接收到信号和上述2个参数后做出相应处理。
  3. 第3行代码是错误处理函数。当网络通信发生错误时,系统发出error(QNetworkReply::NetworkError)信号,可在slotError(QNetworkReply::NetworkError)函数中处理错误。

在上述代码中,有的connect函数有SIGNAL和SLOT宏,将信号和槽函数括起来,如SIGNAL(error(QNetworkReply::NetworkError)),但有的函数没有使用这些宏。Qt5之前要求必须使用SIGNAL和SLOT宏,但Qt5之后允许不加。

实际上,SIGNAL和SLOT宏的定义为空,即程序中SIGNAL和SLOT宏不执行任何代码,只是一种标识,提供给编译器。

功能模块和类

使用上述类和函数就可以实现网络通信,完成文件下载的功能。但对books项目来说,需要下载的文件比较多,除了升级文件,还有升级版本控制文件、其他配置文件等。因此,需要制作一个通用的下载类,将网络下载功能代码打包成一体,主要代码如下:

// books.pro文件
Qt          += core gui network

// UpdateByNetwork.h文件
class UpdateByNetwork : public QObject
{
        Q_OBJECT
public:
        UpdateByNetwork();
        ~UpdateByNetwork();
// ...
}

// UpdateByNetwork.cpp文件
void UpdateByNetwork::startDownload()
{
        // 开始下载
        bDownloaded = false;
        QUrl url = QUrl(baseAddress + downloadFileName);
        QString strAppDir = QCoreApplication::applicationFilePath();
        strAppDir = strAppDir.left(strAppDir.lastIndexOf("/"));
        QDir mydir(strAppDir);    // = QDir::current();
        mydir.mkdir(LOCALUPDATEDIR);        // 将文件下载到指定目录
        pFile = new QFile(LOCALUPDATEDIR + "/" + downloadFileName);
        if(!pFile->open(QIODevice::WriteOnly | QIODevice::Truncate)){
                qDebug() << "本地文件无法创建,无法下载文件。";
                return;
        }
        pManager = new QNetworkAccessManager();
        pReply = pManager->get(QNetworkRequest(url));
        // 创建信号机制
        connect(pReply, QNetworkReply::finished, this, UpdateByNetwork::myFinished);
        return;
}

void UpdateByNetwork::myFinished()
{
        if(pFile){
                pFile->write(pReply->readAll());
                pFile->flush();
                pFile->close();
                delete pFile;
                pFile = nullptr;
        }
        pReply->deleteLater();
        pReply = nullptr;
        bDownloaded = true;
        // QMessageBox::information(this, "完成", "本地下载完成");
}

生成UpdateByNetwork.h和UpdateByNetwork.cpp文件后,要在books.pro文件中添加“network”关键字。同时在UpdateByNetwork.cpp文件中要引用<QtNetwork>头文件,这样Qt程序才能正式使用网络服务功能。

生成自定义类时必须使用public QObject派生,同时要在类定义的首行处添加Q_OBJECT宏。这两处代码的主要目的是允许自定义类使用Qt提供的信号与槽功能。如果疏忽这一点,程序编译时就会出现非常奇怪的错误,这点需要提前注意。

UpdateByNetwork::startDownload()函数中,程序允许用户指定下载文件的具体目录和文件名,调用函数前应事先定义,然后在startDownload()函数内部进行统一处理。函数UpdateByNetwork::myFinished()处理了下载结束后的清理工作。

最后在dialog.h中加入#include "updatebynetwork.h",然后定义一个升级用的类变量UpdateByNetwork *pUpdate,并在dialog.cpp中进行实例化:pUpdate = new UpdateByNetwork(),指定下载目录和文件名后开始下载。

该部分具体代码请参考GitHub—initxuan::books

Qt获取应用程序本地目录的函数有多种,包括QDir::currentDir()QCoreApplication::applicationDirPath()QCoreApplication::applicationFilePath()等。本程序中使用的是QCoreApplication::applicationFilePath()函数,获取目录最后有一个字符“/”,将其删除后获得应用程序目录。

如果直接单击应用程序EXE执行程序,则上述函数功能完全一样;如果通过其他方式执行程序,则程序运行结果将会出现差别,具体如下:

QDir::currentDir()获取的是执行环境的当前目录,也就是说,如果应用程序在Windows开始菜单中有快捷方式,那么利用快捷方式启动程序时,QDir::currentDir()获得的目录是开始菜单中的目录,类似“C:\ProgramData\Microsoft\Windows\Start Menu\Programs”。QCoreApplication::applicationFilePath()函数一般情况下获取的是应用程序的真实目录。经过调试,目前最佳方案是使用QCoreApplication::applicationFilePath()函数,建议直接使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容