需求描述
一直用Qt开发PC版本的程序,常用的方法都是打包发布。目前开发的程序比较复杂,涉及到上百个独立的进程以及4000多个相关文件,采用原来的打包发布就变得很麻烦,第一改动比较频繁,打包次数过多;第二安装包过大,用户那边更新也很麻烦;第三,更新文件的维护也麻烦。所以,支持在线升级,就变得很重要,通过两个版本的迭代设计,设计了一款通用在线升级系统,目前来看效果还是相当不错。
效果图
使用流程:
- 将程序的第一个版本打包成一个安装包,其中包括升级客户端程序;
- 需要更新的时候,将文件更新到服务器中,并提交更新,可以在版本信息中,提交升级说明;
- 客户端点击检查升级,会获取此次需要更新的文件以及缺失的文件,并从服务器上下载;
- 客户端不仅能够用于升级,也可用于安装文件的恢复。
设计要点
- 需要一个有公网地址的云服务器,用作升级服务器;
- 文件的更新检查通过MD5值来校验;
- 文件的更新类型分为两种。一种是覆盖更新,覆盖原有文件;一种是跳过更新,如果文件存在则跳过,不存在则增加,主要是针对于用户的一些配置文件;
- 文件的下载通过sftp协议;
- 需要更新的文件,需要拷贝到云服务器上;
- 服务端,点击检查修改以后,将当前目录下所有的文件都进行MD5值检查并与数据库中的MD5进行比较,如果数据库中不存在则为新增,MD5值不一致则为文件已修改,通过提交按钮,服务端更新数据库中的文件信息以及版本号自动累加;
- 客户端首先获取本地数据库中的版本信息,并从服务端获取本地版本与最新版本之间需要更新的文件信息,并更新本地数据库;
- 客户端依据最新的本地数据库,比对校验本地文件的MD5值,从而生成文件更新列表;
- 客户端依次从服务端下载更新文件;
- 完成系统的升级;
- 此系统也存在一些不足,升级前没有对原有文件进行备份,不能回退版本。
核心代码
//文件的MD5值计算
QString CDBOper::FileMD5(const QString &filePath)
{QString MD5;QFile file(filePath);if(file.open(QIODevice::ReadOnly)){QByteArray str;str = QCryptographicHash::hash(file.readAll(),QCryptographicHash::Md5);MD5.append(str.toHex());}return MD5;
}//文件的递归检查
int CCheckPage::FindFile(const QString &_filePath)
{QDir dir(_filePath); //QDir的路径一定要是全路径,相对路径会有错误if(!dir.exists())return -1;//取到所有的文件和文件名,去掉.和..文件夹dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);dir.setSorting(QDir::DirsFirst);//将其转化为一个listQFileInfoList list = dir.entryInfoList();if(list.size()<1)return -1;int i = 0;//采用递归算法do {QFileInfo fileInfo = list.at(i);bool bisDir = fileInfo.isDir();if(bisDir){FindFile(fileInfo.filePath());}else{QString fileName = fileInfo.filePath().mid(m_prefLen);QString md5 = m_dbOper->FileMD5(fileInfo.filePath());QString md50 = m_dbOper->FileExits(fileName);if(md50.isEmpty()){json_object obj;obj.insert("status", "add");obj.insert("name", fileName);obj.insert("type", fileInfo.suffix());obj.insert("install", m_dbOper->FileInstall(fileName));obj.insert("md5", md5);m_array.append(obj);}else if(md5 != md50){json_object obj;obj.insert("status", "changed");obj.insert("name", fileName);obj.insert("type", fileInfo.suffix());obj.insert("install", m_dbOper->FileInstall(fileName));obj.insert("md5", md5);m_array.append(obj);}}++i;}while(i<list.size());return 0;
}