客户端只要在启动时把自己的版本号报给服务端,服务端就会算出最省流量的升级方式 —— 该给增量包就给增量包,该给完整包就给完整包,跨几个版本都能一步升到最新版。 再加一个「修复」按钮,装坏了也能一键回到官方最新状态。
三步,最短只需要记住一条地址。
在控制台选中应用,页面顶部和应用卡片上都能看到短码。下面这条地址里的 app1 就是它:
把 {当前版本} 换成客户端自己的版本号(写 3.0、3.01、3.1.1 都认,
服务端会归一化)。响应里的 recommended 就是服务端替你挑好的那个包,
直接下载它就行 —— 不用自己判断该用增量包还是完整包。
解压升级包,把 files/ 里的文件按原相对路径覆盖到安装目录,
按 update.json 的 removed 删掉多余文件,
最后把版本号记成响应里的 latest。完整代码见 第 ⑥ 节。
recommended.url → 按同一套规则覆盖。
带 公开 的都不需要登录,客户端直接调。
| 作用 | 地址 | 说明 |
|---|---|---|
| 检查更新 公开 | /u/{code}?c={版本} |
最常用的一个。带上当前版本号,返回是否有新版、推荐哪个包、包的直链与校验值。 |
| 修复 公开 | /r/{code} |
「修复」按钮用。不管当前是什么版本,直接返回最新完整包的直链。 |
| 最新清单 公开 | /m/{code} |
最新版全量文件清单(路径 + sha256 + 大小)。用于本地体检、只补缺失文件、升级后终检。 |
| 下载包 | /d/{code}/{文件名} |
升级包直链。一般不用自己拼,响应里已经给了完整 url。 |
| 兼容写法 | /api/latest?app=¤t=/api/repair?app=/api/manifest?app= |
与上面的短地址完全等价。app 支持短码、应用目录名、应用中文名三种写法(老地址永久可用)。 |
| 版本号进路径 | /u/{code}/{版本} |
连 query 都省了,例:/u/app1/3.0。 |
strategy=auto|patch|full 强制指定升级方式(一般不用传,服务端自己判断最省的那条);
auto=0 表示「只查已经生成好的包,不要在服务端现算」——适合客户端做超时很短的健康检查。
下面是真实接口返回的完整响应(把 URL 的 ?app= 换成你的应用短码,这里就会实时显示你的应用)。
加载中…
| 字段 | 类型 | 说明 |
|---|---|---|
updateAvailable | bool | 要不要升级。false 就直接跳过,其它字段都不用看。 |
current | string | 归一化后的客户端版本(3.0 → 3.0.0)。没传就是 null。 |
latest | string | 最新版本号(归一化)。升级成功后把本地版本号写成它。 |
latestRaw | string | 发布时用的原始写法(3.1),想展示给人看时用这个。 |
| strategy | string | 服务端算出来的升级方式:patch 增量包 / full 完整包 / none 不用升。 |
| recommended | object | 推荐下载的那个包。就是 patch 或 full 里的一个,字段完全一样。客户端只读这一个字段最省事。 |
recommended.url | string | 包直链,直接下载。(recommended 为 null 时才需要看 full) |
recommended.sha256 | string | 整个包的 sha256,下完先校验再解压。 |
recommended.size | number | 字节数。humanSize 是给人看的(2.4 MB)。 |
recommended.type | string | patch 或 full。想显示「本次为增量更新,仅 2.4 MB」就可以用。 |
recommended.from / to | string | 这个包从哪个版本升到哪个版本。增量包的 to 恒等于 latest。 |
steps[] | array | 推荐路径上要按顺序应用的包。当前实现长度恒为 1(一步到位),客户端按数组遍历写就行,将来扩展不用改代码。 |
patch / full | object | 两种包各自的详情。老客户端读这两个字段,行为与以前完全一致(永久兼容)。 |
plan | object | 规划依据:estimateHuman 预计体积、savedHuman/savedPercent 比完整包省了多少、reason 为什么这么选、fullBytes 完整包多大。 |
reason | string | 人话解释,例:「增量包只装变化过的 3 个文件,比完整包省 82.4 MB」。可以直接打到日志里。 |
allVersions | array | 服务端登记过的所有版本(升序)。 |
repairUrl / manifestUrl | string | 修复接口与清单接口的完整地址,省得客户端自己拼。 |
checkUrl / shortUrl | string | 以后要用哪条地址来检查更新(checkUrl 里带 {当前版本} 占位)。 |
install | object | 拆包约定(metaFile/payloadDir/howto[]/notes[]),客户端可以照它做通用实现,不用写死在文档里。注意这里的 howto 是文字步骤,和顶层那个 steps(要按顺序下载的包数组)不是一回事。 |
照着做就不会出错。
version.txt / 注册表 / 配置文件里读,格式随意(3.0、3.01、3.1.1 都行)。第一次装的新用户别跳过升级检查,否则永远不知道自己不是最新。
GET {base}/u/{code}?c={当前版本}。记得 URL 编码版本号;超时建议 15~30 秒;失败就静默跳过,别弹错误框挡住用户启动。
updateAvailable === false → 结束。注意服务端已经做过版本比较,客户端不要自己再比一次(版本号写法歧义太多,容易比错)。
recommended;它是 null 时再退到 full。下载到临时文件,并显示进度。包可能几十到几百 MB,用流式下载(ResponseHeadersRead / stream),别一次性读进内存。
recommended.sha256 比(忽略大小写)。不一致就删掉重下一次,连续失败就提示用户用「修复」。
files/ 下的内容覆盖到安装目录;
然后按 removed 删文件(必须先做路径穿越校验:路径里含 .. 就跳过)。
千万不要边解压边覆盖 —— 中途失败会留下半个版本。
latest(或包内 update.json 的 to,两者一致)。
删掉临时文件。如果升级包里含正在运行的可执行文件,先退出主进程再替换,或替换后用
「重命名旧文件 + 写新文件 + 下次启动清理」的方式绕过文件占用。
strategy=full),因为这时候多个步骤只会更容易出错。
也就是说:你永远不需要自己决定用增量还是完整。
升级包就是一个 zip,固定两样东西,增量包和完整包结构完全一样。
包.zip ├─ update.json ← 元数据:版本、要写哪些文件、要删哪些文件、目标版本全量清单 └─ files/ ← 真正要覆盖到安装目录的内容(保持原相对路径) ├─ App.exe ├─ lib/core.dll └─ docs/readme.md
| 字段 | 含义与用法 |
|---|---|
type | patch 增量包 / full 完整包。客户端处理方式完全一致,不用分支。 |
from / to | 起始版本 / 目标版本。完整包的 from 是 null。to 就是升完之后的版本号。 |
payloadDir | 载荷目录名,固定 files。用它拼路径,别写死字符串。 |
files[] | 本包携带的文件 {path, sha256, size}。要写入的清单(增量包只有变化过的)。 |
removed[] | 要删除的相对路径。旧版有、新版没有的文件都在这。完整包恒为空。 |
manifest[] | 目标版本的全量文件清单(路径 + sha256 + 大小)。这是「升完就是最新版」的证据:覆盖 + 删除后逐条比对即可确认。 |
stats | 统计信息(文件数、payload 大小、完整包大小、省下的比例),可用来展示。 |
files/ 和 removed 都为空是合法的 —— 只有版本号变化时会这样。
照常走完流程、写入新版本号即可,别当成错误。
都是完整可用的最小实现,把 APP_CODE 换成你的短码就能跑。
加载中…
用到的命名空间:System.Net.Http、System.IO.Compression(需 System.IO.Compression.FileSystem)、System.Security.Cryptography、System.Text.Json。
加载中…
Electron 里建议在主进程做升级(有 fs 权限),进度通过 webContents.send 发给渲染进程显示。
加载中…
纯标准库实现,不需要额外依赖。
装坏了、文件被删了、版本对不上,用户点它就能回到官方最新状态。
这个接口不看客户端版本,永远返回最新完整包的直链(full.url)。
完整包自带全量清单,所以修复逻辑比升级更简单:
/r/{code}full.url / full.sha256 / fileCount(将恢复多少个文件)。files/ 覆盖安装目录full.toremoved 是空的,服务端不会让你去删东西。
如果想做一个「彻底干净」的修复,可以先拉 /m/{code} 拿到官方全量清单,
把安装目录里不在清单中的文件删掉 —— 但要排除用户数据、存档、配置、日志、插件目录,
否则会把用户的东西删掉。默认建议不要做这件事。
调 /m/{code} 拿清单,逐个算本地文件的 sha256:一致的跳过,缺失或对不上的记下来。
若缺失文件总量很小,可以只从完整包里取这几个文件覆盖(完整包仍然是 zip,解压后按需取用即可)。
包体积不大的应用没必要这么麻烦,直接整包覆盖最稳。
三层保险,按需要挑。
| 哪一层 | 做什么 | 必要性 |
|---|---|---|
| 包级校验 | 下载完算整个 zip 的 sha256,与 recommended.sha256 比对。 |
必做 |
| 文件级校验 | 覆盖完成后,按包内 update.json 的 manifest 逐条算本地文件 sha256 比对(或只抽查关键文件)。 |
推荐 |
| 版本自检 / 自愈 | 启动时调 /m/{code},本地抽检发现缺失或损坏 → 自动走「修复」流程静默补回。 |
推荐 |
| 降级 / 回滚 | 升级失败时把临时目录里备份的旧文件还原,保持原版本可运行,下次再试。 | 建议 |
Updater.exe,主程序调用它后自己退出,由它完成替换再启动主程序;
② 或者下载到临时目录,注册「下次启动前替换」的钩子。
3.0 → 3.0.0、3.01 → 3.0.1、3.1 → 3.1.0、3.11 → 3.1.1。唯一的歧义是 3.10 会等于 3.1,位号接近 10 时请写成三段式 3.10.0。
strategy 置为 full 并给出完整包。reason 里会写明原因。客户端不需要写特殊分支,照常下载 recommended 即可。
recommended.to 恒等于 latest。
plan.reason 会写清楚,例如「增量包只比完整包小 1.2 MB,不值得多走一步」。
patch / full 字段永久保留、语义不变。新字段只增不改。所以客户端可以放心依赖 recommended / steps,也可以继续用老字段。
url(已经是当前域名),换域名/换短码都不用改客户端。
{当前版本} 换成真版本号)就能看到完整 JSON;控制台里也能看到每个应用的短码、直链和包体积。reason 字段会用人话解释服务端为什么这么选包。
本页地址可以直接分享给做客户端的同事:
/guide