Electron客户端自动更新简介
Windows 上用 electron-builder 打 NSIS 包,electron-updater 走 HTTPS 静态源更新。差分只少下数据块,形态仍是全量覆盖安装,并说明 latest.yml 与 Nginx。
Electron客户端自动更新简介
桌面端用 electron-builder 打 Windows NSIS 安装包,用 electron-updater 走 generic HTTPS 静态源做自动更新。本文说明能力边界(blockmap 差分下载仍是全量安装)、
latest.yml发版目录、客户端检查 / 下载 / 安装流程、Nginx 配置,以及本地 SQLite 与升级的协同。流量上可以少下一些块;产品形态仍是「下载完整安装包再覆盖安装」。
参考与延伸阅读:
- electron-builder 自动更新:https://www.electron.build/auto-update
- Publish 配置(含 generic):https://www.electron.build/configuration/publish
- Electron 官方更新教程:https://www.electronjs.org/docs/latest/tutorial/updates
目录
- 1. 能力边界
- 2. blockmap 差分下载
- 3. 本地库结构迁移
- 4. 更新配置与构建产物
- 5. 服务端静态发版
- 6. 客户端检查下载与安装
- 7. Nginx 静态源配置
- 8. 正式环境启用清单
- 9. 路径与运维注意
- 10. 小结
1. 能力边界
| 要点 | 结论 |
|---|---|
resources/app-update.yml | 打包后的自动更新配置:provider、查询 URL、本机缓存目录名 |
| 是否用于更新 | 是,供 electron-updater 读取 |
| 文件级热补丁 | 不支持;不能只换 app.asar 或某个 dll 就完成升级 |
| 独立「小补丁包」合并进安装目录 | 默认不支持,需自研补丁合并器 |
| Windows 差分 | 有完整安装包与 .blockmap 时,可只拉变化数据块,本地合成后再跑 NSIS |
| 服务端形态 | HTTPS 静态目录(或对象存储 + CDN)托管 latest.yml、安装包、.blockmap;不必为检查更新单独写业务 API |
| 是否有新版本 | 只认 {url}/latest.yml 的 version,与本地 app.getVersion() 比较 |
用户感知仍是「下载更新 → 退出安装」,不是进程内热替换。
若未来要真正的小补丁,需要另做架构,例如壳与大资源分离、按组件版本从 CDN 拉取,或自研差分格式与回滚。正式环境建议先落地 generic 静态源 + blockmap。
2. blockmap 差分下载
假设已装 v2.0.0,服务器发布 v2.0.1:客户端用新旧 blockmap 对比,只拉变化块,本地合成完整 setup.exe,再跑 NSIS 覆盖安装。
| 场景 | 差分收益 |
|---|---|
| 只改 Electron 壳或少量前端资源,大体积附属资源未变 | 中等偏大(相对整包) |
| 附属运行时被整体重打包,二进制布局漂移 | 偏小到中等 |
| 升级跨度大,或模型、引擎、vendor 等大资源变更 | 接近全量 |
服务器未上传 .blockmap | 只能全量下载安装包 |
过早删除旧版安装包或 .blockmap | 差分可能失败,回退全量或失败 |
blockmap 按内容块对比,不认「逻辑只改了一行」。PyInstaller、AOT、整体重打 sidecar 一旦重出,即使业务只改几个模块,产物布局也常整段变化。规划带宽与用户体验时,按「用户可能接近全量下载」准备;差分是优化项,不是承诺。
务实做法:
- 仍上传
.blockmap(零成本,大资源未变时能省一截)。 - 发版带宽按接近全量准备。
- 真要小补丁,优先壳与大资源分离,而不是指望 blockmap 单独解决问题。
3. 本地库结构迁移
客户端本地库不在安装包里随 exe「打补丁脚本」下发;数据在用户目录,覆盖安装默认保留。
| 做法 | 是否推荐 | 说明 |
|---|---|---|
安装包外再附独立 .sql,靠运维或用户手跑 | 不推荐 | 桌面端无法保证时机,漏跑则崩溃 |
| 固化在代码里,启动时自动迁移 | 推荐 | 与安装包版本绑定,升级打开即迁移 |
建议约定:
- 加法变更(加表 / 加列 / 加索引):写在启动路径里,幂等执行(
CREATE TABLE IF NOT EXISTS、探测缺列再ALTER TABLE … ADD COLUMN)。 - 显式版本号(中期):
schema_version或PRAGMA user_version,迁移步骤编号;多库可各管各库。 - 破坏性变更(删列 / 改类型 / 拆表):在代码里做「建新表 → 拷数据 → 改名」事务;慎用,需可回滚或至少可跳过坏库并提示。
- 不要把迁移 SQL 只放在发版说明里让用户手跑;也不要依赖「差分包夹带 sql」——更新形态是整包安装,迁移应跟新版本二进制走。
- 与自动更新的关系:装完新版本首次启动 → 新代码跑迁移 → 旧数据保留。迁移失败应打日志并给出可恢复路径(备份 db 文件),而不是卡在半安装状态。
4. 更新配置与构建产物
打包安装后,应用根下常见:
1
<安装目录>/resources/app-update.yml
内容由 electron-builder.yml 的 publish 写入,形如:
1
2
3
provider: generic
url: https://updates.example.com/app-updates/
updaterCacheDirName: my-app-updater
| 字段 | 作用 |
|---|---|
provider: generic | 通用 HTTP(S) 静态源(不是 GitHub Releases) |
url | 更新元数据与安装包所在目录的根 URL |
updaterCacheDirName | 本机更新缓存目录名(Windows 多在 %LOCALAPPDATA%\<name>-updater) |
开发态可用 dev-app-update.yml,配合 forceDevUpdateConfig 在未打包时指向同一套更新源做联调。
构建产物文件名由 artifactName 等配置决定,常见约定:
1
2
3
my-app-${version}-setup.exe
my-app-${version}-setup.exe.blockmap
latest.yml
版本号与 package.json 的 semver 对齐;若带 build metadata,以实际 dist/ 文件名为准。
5. 服务端静态发版
| 需要 | 不需要 |
|---|---|
| 可 HTTPS 访问的静态站点(Nginx / OSS+CDN / 对象存储静态托管) | 为「检查更新」单独写 REST API |
每次发版放齐 latest.yml、安装包、.blockmap | 用数据库存版本号(客户端只认 latest.yml) |
| 正确的 MIME、Range(差分下载)、缓存策略 | 把未签名内测包裸放公网 |
业务 API(登录、会员、强制最低版本)是另一条线:解决「太旧禁止用」;自动更新解决「有新包可装」。更新源通常公开可读。
目录约定:
1
2
3
4
5
6
7
8
https://updates.example.com/app-updates/
├── latest.yml
├── my-app-2.0.0-setup.exe
├── my-app-2.0.0-setup.exe.blockmap
├── my-app-2.0.1-setup.exe
├── my-app-2.0.1-setup.exe.blockmap
├── my-app-2.0.2-setup.exe
└── my-app-2.0.2-setup.exe.blockmap
要点:
latest.yml每次发版覆盖写,始终指向当前最新。- 历史 setup 与 blockmap 暂时保留(建议至少最近 2~3 个正式版),方便差分与补装。
latest.yml内path/files[].url必须与目录里真实文件名一致。
发版清单:
1
2
3
4
5
1. 改 electron-builder.yml publish.url → 正式 HTTPS 地址
2. 打 Windows 安装包
3. 上传 dist/ 中的 latest.yml、setup.exe、.blockmap
4. 用浏览器或 curl 验证三者均为 200,且 latest.yml 的 version 正确
5. 用低版本客户端走检查 → 下载 → 安装
发 2.0.2 后,latest.yml 示意(字段以 electron-builder 实际生成为准):
1
2
3
4
5
6
7
8
version: 2.0.2
files:
- url: my-app-2.0.2-setup.exe
sha512: <base64>
size: 185000000
path: my-app-2.0.2-setup.exe
sha512: <base64>
releaseDate: '2026-07-20T02:00:00.000Z'
size 为示意字节数。客户端比较本地 app.getVersion() 与 latest.yml 的 version,有更新则按 path 下载。
6. 客户端检查下载与安装
electron-updater 常见接法:
autoDownload = false;收到update-available且用户未关闭自动更新时,由业务代码调用downloadUpdate。autoInstallOnAppQuit = false:下完不自动装,等用户确认后quitAndInstall。allowPrerelease = false。- 设置项关闭自动更新时可跳过启动检查,且不自动下载;仍可提供手动「检查更新」。
- 主进程与渲染进程通过 IPC 协作,常见通道:
update:check/update:download/update:status/update:install。
6.1 启动自动检查
打包态(已安装包,非纯开发态):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
应用启动
│
├─ setupAutoUpdater()
│ ├─ 读 resources/app-update.yml(或开发态 forceDevUpdateConfig)
│ └─ 绑定 checking / available / progress / downloaded / error
│
├─ 自动更新已关闭?
│ └─ 是 → 跳过启动检查(仍可手动检查)
│
└─ 否 → checkForUpdates()
│
▼
GET {url}/latest.yml
│
├─ 网络或证书失败 → updater error
├─ version ≤ 当前 → update-not-available
└─ version > 当前 → update-available
│
├─ 通知 UI
└─ 自动更新已开启?
├─ 否 → 等用户点下载
└─ 是 → downloadUpdate()
6.2 下载
1
2
3
4
5
6
7
8
9
10
11
12
13
downloadUpdate()
│
├─ 请求 latest.yml 中的 setup.exe(及 blockmap)
│ ├─ 有旧版缓存且新旧 blockmap 可用
│ │ └─ 差分:只拉变化块 → 本地合成完整 setup.exe
│ └─ 否则全量下载 setup.exe
│
├─ download-progress → UI / 任务栏进度
│
└─ update-downloaded
├─ 包落在本机 updater 缓存目录
├─ 通知 UI「已下载,可安装」
└─ 等待用户确认(不自动退出安装)
6.3 安装
1
2
3
4
5
6
7
8
9
10
用户确认安装(UI → update:install)
│
▼
quitAndInstall(false, true)
│
├─ 退出当前进程
├─ 拉起已下载的 NSIS setup.exe(覆盖安装)
│ └─ 默认保留用户数据(数据在用户目录,不在安装目录)
│
└─ 安装结束 → 再打开 → 版本变为新 version
6.4 版本演进
1
2
3
4
5
6
7
8
时刻 A 用户安装 2.0.0;latest.yml → 2.0.0
时刻 B 运维上传 2.0.1 的 exe 与 blockmap,覆盖 latest.yml
保留 2.0.0 文件利于差分
2.0.0 客户端检查后下载并安装 → 2.0.1
时刻 C 再发 2.0.2,覆盖 latest.yml;建议仍保留 2.0.1
仍停在 2.0.0 的用户一次检查即可直达 2.0.2,不必先装 2.0.1
2.0.0 → 2.0.2 跨度大则差分接近全量
2.0.1 → 2.0.2 差分通常更划算
6.5 与强制最低版本的关系
1
2
3
4
5
6
7
自动更新线 业务版本线(登录 / 业务 API)
│ │
▼ ▼
latest.yml 有更新包 服务端判定版本过低
→ 引导下载安装 → 禁止登录 / 提示升级
│ │
└──────── 最低版本不得高于当前 latest ─┘
两条线职责不同。产品侧需约定最低版本不高于当前 latest.yml 所指向的版本,否则用户会被拦却装不到新包。
7. Nginx 静态源配置
假设:
- 域名:
updates.example.com - 物理目录:
/var/www/app-updates/ - 客户端
publish.url:https://updates.example.com/app-updates/ - 已上传 2.0.0 / 2.0.1 / 2.0.2 的 exe 与 blockmap,
latest.yml指向 2.0.2
避免 alias 与正则组合踩坑,用 root + 前缀:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
listen 443 ssl http2;
server_name updates.example.com;
root /var/www;
location /app-updates/ {
client_max_body_size 2g;
location = /app-updates/latest.yml {
add_header Cache-Control "no-cache, must-revalidate" always;
default_type text/yaml;
}
location ~* \.(exe|blockmap)$ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
add_header Accept-Ranges bytes always;
default_type application/octet-stream;
}
}
}
磁盘布局:
1
2
3
4
5
6
7
8
/var/www/app-updates/
├── latest.yml
├── my-app-2.0.0-setup.exe
├── my-app-2.0.0-setup.exe.blockmap
├── my-app-2.0.1-setup.exe
├── my-app-2.0.1-setup.exe.blockmap
├── my-app-2.0.2-setup.exe
└── my-app-2.0.2-setup.exe.blockmap
| 步骤 | 发布 2.0.1 | 再发 2.0.2 |
|---|---|---|
| 1 | 上传 2.0.1 的 exe 与 .blockmap | 上传 2.0.2 的 exe 与 .blockmap |
| 2 | 覆盖 latest.yml(version=2.0.1) | 再覆盖(version=2.0.2) |
| 3 | curl 确认 latest.yml | 确认已是 2.0.2 |
| 4 | 保留 2.0.0 文件 | 至少保留 2.0.1;2.0.0 可按策略延后删除 |
| 5 | 用 2.0.0 客户端测升级 | 用 2.0.0 与 2.0.1 各测一次到 2.0.2 |
校验:
1
2
3
4
curl -fsSL https://updates.example.com/app-updates/latest.yml
curl -I https://updates.example.com/app-updates/my-app-2.0.2-setup.exe
curl -I -H "Range: bytes=0-1023" \
https://updates.example.com/app-updates/my-app-2.0.2-setup.exe
后两条应分别看到 200、以及 206 Partial Content(或等价的 Range 支持)。
electron-builder.yml:
1
2
3
publish:
provider: generic
url: https://updates.example.com/app-updates/
开发态测同一源时同步 dev-app-update.yml。改完 URL 必须重新打安装包:已装旧包里的 app-update.yml 仍是旧地址。对象存储 + CDN 的等价要求:关闭 latest.yml 长缓存、开启 HTTP Range。
8. 正式环境启用清单
- 准备 HTTPS 静态目录(Nginx 见上一节,或 OSS+CDN 等价配置)。
- 修改
publish.url与开发态更新配置。 - 打安装包,上传
latest.yml、exe、blockmap。 - 确认新安装包内
resources/app-update.yml已是正式地址。 - 低版本客户端验证:检查 → 下载 → 安装。
- 与强制最低版本、发版节奏、是否隐藏「检查更新」入口一并定稿。
9. 路径与运维注意
| 位置 | 说明 |
|---|---|
安装目录 resources/app-update.yml | 打包后运行时更新配置 |
electron-builder.yml → publish | 构建时写入更新源 |
dev-app-update.yml | 开发态更新源 |
| 主进程 updater 模块 | 检查 / 下载 / 安装逻辑 |
| 本机 updater 缓存 | 大致 %LOCALAPPDATA%\<updaterCacheDirName> |
| 用户数据 | 通常在用户目录,覆盖安装默认保留 |
运维注意:
- 务必同时上传 blockmap,否则 Windows 只能全量下安装包。
- 不要过早删旧版 exe / blockmap;建议至少留最近 2~3 个正式版。
- HTTPS 证书须有效;自签易导致检查或下载失败。
latest.yml不要长缓存;安装包文件名带版本,可长缓存。- 开启 HTTP Range(206),否则差分能力打折。
- 更新与「服务端强制最低版本」是两条线:最低版本不得高于当前 latest。
- 安装包很大时优先保证带宽;差分只减轻大资源未变时的下载量。
- 改
publish.url后必须重新打包装发;旧包不会自动改更新源地址。
10. 小结
| 要点 | 结论 |
|---|---|
| 产品形态 | 全量 NSIS 覆盖安装,不是热更新 |
| 差分 | Windows blockmap 可少下数据块,规划仍按接近全量 |
| 服务端 | HTTPS 静态目录即可,核心是 latest.yml |
| 客户端 | 比较 app.getVersion() 与 latest.yml 的 version |
| 本地库 | 启动时代码迁移,不要靠手跑 SQL |
| 配置变更 | 改更新源 URL 后须重打安装包 |
