Electron 自動更新,繞過 latest.yml 使用自定義接口
大家好,我是楊成功。
在上一篇文章《手擼 Electron 自動更新,再繁瑣也要搞懂它》中,我們詳細介紹了 Electron 自動更新的全流程。
簡單來說,就是打包生成 latest.yml 文件和安裝包,并上傳到服務器??蛻舳舜蜷_時訪問服務端的 latest.yml 地址,判斷是否有新版本,有則自動下載新包并更新。
對于以上的更新流程,很多朋友在評論區留言表示不夠靈活,比如:
- 只能自動更新嗎?能不能通過后端接口來判斷是否更新?
- 必須要用 latest.yml 嗎?能不能繞開它?
- 如何區分 Window、Mac、Mac M1 三種安裝包?
下面我們一一解答這些問題,并手擼一個更靈活的自動更新接口。
如何本地測試更新?
在開發階段想測試一下檢測更新的流程走沒走通,可能不太好測試,因為 Electron 默認在開發環境下會繞過更新檢測。
開發環境下 Electron 啟動后,如果接入了自動更新,主進程控制臺會打印下面的信息:
Skip checkForUpdates because application is not packed and dev update config is not forced
意思是當前是開發環境,未打包,所以繞過檢測。Electron 通過 app.isPackaged
的值來判斷是否打包,那么在開發環境下,我們可以修改一下這個值:
import { app } from 'electron';
// 未打包時是開發環境
if (!app.isPackaged) {
Object.defineProperty(app, 'isPackaged', {
get: () => true,
});
}
重新運行,大概率會看到第二個錯誤:
Error: ENOENT: no such file or directory /xxxx/app-update.yml
因為沒有打包嘛,所以找不到 app-update.yml
這個文件,索性我們就創建一個。
在根目錄下創建一個 dev-update.yml
文件(文件名可自定義),寫入配置:
provider: generic
updaterCacheDirName: demo-updater # 下載目錄
然后在開發環境指定這個配置文件地址:
import { app } from 'electron';
import path from "path";
if(!app.isPackaged) {
...
autoUpdater.updateConfigPath = path.join(__dirname, "../../dev-update.yml");
}
重新運行項目,會發現檢測更新的邏輯可以正常執行了。
能不能繞開 latest.yml,走后端接口?
可能大家希望的檢查更新流程是這樣:
調用后端的 API 接口,接口返回 JSON 格式數據,包含最新的版本號和安裝包下載地址。將該版本號與本地版本號做對比,如果不一樣則表示有更新,并執行下載。
然而 electron-updater
是通過 latest.yml
文件來獲取版本號等信息。latest.yml 是一個配置文件,內容如下:
version: 1.0.2
files:
- url: elapp_1.0.2.exe
sha512: xxxxxx
size: 72716511
path: elapp_1.0.2.exe
sha512: xxxxxx
releaseDate: '2023-11-29T02:28:28.032Z'
大家想繞過它,可能是因為 YAML 文件的內容看不太懂,或者與接口格式不匹配。其實它就是一個普通的配置文件,轉換成 JSON 格式如下:
{
"version": "1.0.2",
"files": [
{
"url": "elapp_1.0.2.exe",
"sha512": "xxxxxx",
"size": "72716511"
}
],
"path": "elapp_1.0.2.exe"
}
可能有人會問:配置文件可以改成latest.json
嗎,這樣后端就可以動態返回了。
我仔細查閱過文檔,目前只支持 YAML 文件,不支持 JSON,所以 latest.yml 無法繞開。但這只是官方說法,咱還是有辦法滴。
經過大量測試,我發現把 latest.yml 文件的內容手動改成 JSON 格式也是可以的。這樣的話,就可以寫一個接口來模擬 latest.yml 的地址。
假設 latest.yml 的訪問路由是/ele-app/latest.yml
,那么寫一個接口如下:
const app = require('express')()
app.get('/ele-app/latest.yml', (req, res, next) => {
let resinfo = {
version: "1.0.25",
path: "xxx_1.0.25.exe"
sha512: "xxxxxx"
}
res.send(resinfo)
})
該接口就是自定義的檢測更新接口。接口返回值中至少要包含version、path、sha512
三個屬性(與 latest.yml 中的配置保持一致)。這樣我們不需要上傳 latest.yml 文件了,用該接口替代即可。
基于該檢測更新接口,接下來我們逐步實現自定義更新流程。
如何區分 Windows 和 Mac 系統
對于 Windows 和 Mac 兩個系統的更新,electron-updater 使用不同的配置文件,分別是 latest.yml
和 latest-mac.yml
。
從上一步的檢測更新接口來看,不同的配置文件就是不同的路由,我們改造接口如下:
app.get('/ele-app/:platform', (req, res, next) => {
let { platform } = req.params
let resinfo = null
// 返回 Windows 配置
if(platform == 'latest.yml') {
resinfo = {
version: "1.0.2",
path: "xxx_1.0.2.exe"
sha512: "xxxxxx"
}
}
// 返回 Mac 配置
if(platform == 'latest-mac.yml') {
resinfo = {
version: "1.0.3",
path: "xxx_1.0.3.dmg"
sha512: "xxxxxx"
}
}
if(!resinfo) {
resinfo = { code: 400, msg: '參數錯誤' }
}
res.send(resinfo)
})
上面代碼中,使用動態路由返回 Windows 和 Mac 的配置,同時兼容了兩個平臺的更新檢測。
完整的自定義更新流程
經過上面的介紹,自定義檢測更新的關鍵思路已經講清楚了。完整的更新流程如下:
(1)打包各個平臺的安裝包,上傳服務器。
如何打包在上一篇介紹過,就不展開說了。注意的是:現在你只需要上傳安裝包,不需要上傳 latest.yml 文件。
(2)在主進程中設置更新地址,并手動控制更新。
假設我們編寫的檢測更新接口已經部署,設置方法如下:
import { autoUpdater } from 'electron-updater';
// 設置檢測更新的地址
autoUpdater.setFeedURL('http://[xxx]/ele-app');
// 不自動下載
autoUpdater.autoDownload = false;
// 觸發檢測
autoUpdater.checkForUpdatesAndNotify().catch();
// 監聽到可更新
autoUpdater.on('update-available', (info) => {
// info 是檢測更新接口返回的數據
if (info.can_download) {
// can_download 是自定義屬性
autoUpdater.downloadUpdate();
}
});
(3)編寫檢測更新接口,返回配置。
返回的配置我們可以自定義,假設返回結果如下:
{
"version": "1.0.2",
"path": "http://xxx/xxx_1.0.2.exe",
"sha512": "xxxxxx",
"can_download": false
}
上述示例中,接口返回了自定義屬性 can_download
,我們在第(2)步中使用了該屬性,用于判斷是否執行下載更新。
通過這種方式,即便我們更新了安裝包,也可以自由決定是否要下載安裝。
這里有一個小驚喜:path
屬性的值可以是一個完整的安裝包地址,這樣可以把安裝包上傳到任意地方。如果值是一個文件名,那么會以第(2)步中 setFeedURL()
方法設置的地址為前綴。
提醒:sha512 屬性的值必須從打包生成的 latest.yml 中獲取,不可以隨意寫,否則在安裝時不能通過檢驗,會報這個錯:
Error: sha512 checksum mismatch
特別篇:Mac 如何區分 Intel 和 M1?
Mac 系統有兩種軟件包,分別對應 M1 芯片和 Intel 芯片,兩者不兼容。一般打包時我們也會構建兩種安裝包。
那么在檢測更新時,我們就需要返回適配當前系統的安裝包。但不管是 M1 還是 Intel 都使用 latest-mac.yml
這一個配置文件,該如何區分呢?
這個時候就要從主進程中獲取參數,然后傳給接口了。步驟如下:
(1)在主進程中,獲取系統架構,并通過請求頭傳給檢測更新接口。
import { autoUpdater } from 'electron-updater';
// 添加請求頭
autoUpdater.requestHeaders = {
elearch: process.arch,
};
(2)在接口中接收參數,并在返回 Mac 配置中判斷:
app.get('/ele-app/:platform', (req, res, next) => {
let { platform } = req.params
let { elearch } = req.headers
if(platform == 'latest-mac.yml') {
if(elearch == 'arm64') {
// M1
return {...}
} else {
// Intel
return {...}
}
}
})
有了上面的邏輯,我們在打包時就可以單獨打兩個包,分別上傳,然后在檢測時返回不同的下載地址。
總結
本文使用自定義接口的方式,繞過了 latest.yml 配置文件,使自動更新的靈活性更高,也更符合我們的常規習慣,但是需要維護一個更新接口。
如果不想維護接口,那么上傳 latest.yml 文件的方式更適合你。