在客户端在线更新系统里,流程图并不难画,真正容易踩坑的是“协议”。
如果manifest.json 设计不当,常见问题会集中爆发:
1)版本号比较规则不一致导致误判
2)缺少校验字段导致下载包损坏难以定位
3)强制更新/最低可运行版本策略无法落地
4)后续要做灰度发布、多平台/多架构分发时协议难以兼容扩展
本文基于“Qt6 Host + 独立 Updater” 的更新架构,给出一套可演进的 manifest.json 设计方案,并重点说明版本比较的推荐实现方式。你可以将文中的 manifest 直接部署到 HTTP 服务上,与上一篇的 Check/Apply 更新流程配合使用。
什么是manifest.json?
这里的 manifest.json 指“更新清单(manifest)文件”。
“manifest”本意是清单/声明文件,是行业内常用命名:它不承载更新包本体,而是以一份可通过 HTTP 下发的 JSON 元数据,描述当前可用的最新版本及其发布信息(例如版本号、下载地址、包大小、哈希校验、更新说明、强制更新策略等)。
在本文的体系中,Host 在 Check 阶段读取 manifest.json 以判断是否需要提示更新并更新 UI;独立 Updater 在 Apply 阶段依据同一份 manifest.json 选择下载目标并完成完整性校验与安装执行。
因此,manifest.json 可以视为更新系统的“单一事实来源”(Single Source of Truth):用于将更新策略与发布信息从客户端代码中解耦出来,便于后续运维与扩展。
manifest 设计
设计目标
在定义字段之前,先明确manifest的设计目标。本文的manifest.json需要满足一下几点:
★可用:Host能据此判断是否需要提示更新;Updater能据此完成下载与校验。
★可扩展:后续增加字段不影响旧客户端解析
★可兼容:允许服务端同时服务不同版本客户端
★可校验:更新包必须可校验,避免下载成功但安装失败/跑不起来
字段集合
为了让更新流程先跑通,manifest至少包含以下字段:
| version | 最新版本号(用于版本比较) |
| url | 更新包下载地址(Updater使用) |
| sha256 | 更新包哈希,校验完整性 |
| size | 更新包大小 |
| changelog | 更新说明(Host UI展示) |
这组字段追求“够用、最少”,不追求一次把所有高级能力(灰度、多平台、多包、强更策略、签名等)都设计进去。先用MVP跑通Demo、验证链路,在逐步加字段扩展,而不会推翻原有协议。
注意:很多人只放 version + url,这会导致“包损坏/被替换”时难以定位问题,工程上建议一开始就加入hash+大小校验更安全。
示例
下面给出一个简单的manifest.json的示例结构:
{
"id": "e58e228e2b424aed8a826fa04fef90e6",
"createTime": "2026-01-01 13:23:55",
"version": "2.1.17.10",
"sha256": "YOUR_SHA256_HERE",
"sizeBytes": "445718686",
"changelog": "显示当前更新包的更新说明内容",
"updateUrl": "http://10.0.0.16:5000/update/ProjectName/697100a466eb419b8af08ca58f46f669.exe"
}
字段说明与使用阶段
id(可选)
更新包记录的唯一标识,用于日志追踪/服务端审计;客户端一般不参与逻辑判断。
createTime(可选)
发布时间/生成时间,可用于 UI 展示或排查问题,不参与版本比较。
version(必填)
服务器最新版本号。
Host 在 Check 阶段用它与当前版本比较,决定是否显示“升级”标识;Updater 在 Apply 阶段可二次校验避免重复升级。建议明确版本比较规则(本文后续章节说明)。
updateUrl(必填)
更新包下载地址。
Updater 在 Apply 阶段使用该 URL 下载更新包。
sizeBytes(必填)
更新包大小(字节)。
用于下载进度展示与快速完整性校验。
建议用字符串类型而非数字类型,如果文件过大会导致解析json出现错误,当前QJson不会,如果是CJSON就会有些问题了。
sha256(必填)
更新包哈希值。
Updater 下载完成后用于完整性校验,避免“下载成功但包损坏/被替换”导致安装失败或不可启动。
changelog(可选)
显示当前更新包的更新说明,可根据工程要求是否展示到UI上。
版本比较规则
在更新系统中,版本比较必须是一个“确定性规则”,且 Host(Check 阶段)与 Updater(Apply 阶段)必须使用同一套算法。
常见踩坑是直接用字符串比较版本号,例如把1.10.0误判为小于1.2.0。
因此本文明确约定:版本号由若干段十进制整数构成,使用逐段数值比较。
版本号格式约定
在本文Demo中使用四段版本号(与项目规则一致,无论你是纯Qt开发还是VSCode开发):
version:major.minor.patch.build
示例:2.1.17.10
比较原则
将版本字符串按 “.” 拆分为整数数组,按顺序逐段比较:
1)从major开始比较,第一段更大则版本更大
2)若相等,再比较下一段
3)知道某一段出现差异嚯所有段都相等
段数不一致如处理
为了固定规则,如果段数不一致则认为该软件不具备升级功能。
非法版本号处理策略
manifest版本如果出现非法合适(包含字母、空段、段数过大、溢出等),则认为该软件不具备升级功能。
代码示例
bool IsUpdateAvailable(const QString& currentVersion, const QString& lastestVersion)
{
const QStringList listCurrent = currentVersion.split('.');
const QStringList listLast = lastestVersion.split('.');
//基本校验:不是4段就认为不升级
if (listCurrent.size() != 4 || listLast.size() != 4) {
return false; //不升级
}
for (int i = 0; i < 4; i++) {
bool bOk1 = false, bOk2 = false;
const int current = listCurrent[i].toInt(&bOk1);
const int last = listLast[i].toInt(&bOk2);
//某段不是纯数字,认为不升级
if (!bOk1 || !bOk2) {
return false;
}
if (last > current) {
return true; //远端更新,需要升级
}
if (last < current) {
return false; //当前更高,不升级
}
}
//四段都相等,不升级
return false;
}
在Check/Apply阶段的使用方式
Check(Host)阶段
若currentVersion < manifest.version ⇒ 显示“升级”标识(可选更新),否则不显示
Apply(Updater)阶段
AppUpdater.exe启动后再次获取/读取manifest,比对currentVersion 与 manifest.version:
若无需升级:直接提示“已是最新版本”并退出
若需要升级:进入下载与安装流程
这样可以避免用户点击后,服务端刚好回滚版本/已更新完成导致重复安装。
总结
本文完成了 manifest.json 的字段约定,并统一了四段版本号(如 2.1.17.10)的比较规则。
有了这份协议,Host 在 Check 阶段就能稳定判断“是否需要提示更新”,Updater 在 Apply 阶段也能按同一份数据完成下载与校验。
下一篇将进入实现层:Qt6 宿主如何异步拉取 manifest、解析字段并更新“升级”标识。
我是糯诺诺米团,一名C++程序媛~

38

被折叠的 条评论
为什么被折叠?



