Electron实现在线更新


上周完成了在线更新的功能,感觉有必要记录一下实现的过程。网上大部分说的都是放在githubgitlab上,然后
利用Releases服务进行更新,但是客户大部分都是私有化部署的啊,你去搞什么第三方托管平台,我们只能依靠generic这种方式了,也就是使用 HTTP 服务器作为更新源来升级,这样的话就需要一个文件服务器。我实现过程中还是发现了这篇文章讲的挺好的。
简单且详细地实现 Electron 自动更新(Auto Update)
这里我用了他提供的本地文件服务器,便于我测试。

演示效果

视频效果可以去我公众号搜索这篇文章。可以看到视频

更新服务器

我们需要准备一个文件服务器地址用于存放更新包。可以用上面那个作者提供的代码。我这里直接干脆也放一份在我仓库里吧。
地址: https://gitee.com/pearpear/file-server
yarn之后cd到file-server执行yarn start命令,我在执行过程中遇到了pm2的一些问题,我单独又装了一次pm2就解决了。
运行之后,你看到这样的画面,就说明文件服务器启动成功了。

文件访问地址是这个http://localhost:8099/packages/win32/
到时候我们的安装包就是放这里。

然后在package.json文件中修改publishurlversion版本号(否则检测出来永远是最新的)

1
2
3
4
5
6
// package.json
"version":"1.0.0",
"publish": {
"provider": "generic",
"url": "http://localhost:8099/packages/win32/"
}

核心代码

我把实例直接放在仓库里了。可以自己去拉取调试。
地址: https://gitee.com/pearpear/electron-online-update-demo
electron是禁止在开发环境在线更新的,他需要安装后再去更新,
我在项目里都处理过了,可以在开发环境下,进行在线更新测试,但是他不会安装,需要在生产环境安装,具体看上面视频里的操作。
我们首先安装2个依赖包
yarn add electron-updater electron-log 一个用来更新,一个用来查看日志调试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// autoUpdater.js
const { app, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');

// 检查是否要更新,他会去url查询版本
ipcMain.handle('check-for-updates', async () => {
try {
const result = await autoUpdater.checkForUpdates();
log.info('Check update result:', result);
return result;
} catch (error) {
log.error('Update check failed:', error);
win.webContents.send('update-error', error.message);
throw error;
}
});

// 开始下载最新包
ipcMain.handle('start-download', async () => {
try {
return await autoUpdater.downloadUpdate();
} catch (error) {
log.error('Download failed:', error);
throw error;
}
});

// 下载进度
autoUpdater.on('download-progress', (progressObj) => {
log.info('Download progress:', progressObj);
win.webContents.send('download-progress', progressObj);
});

// 是否下载完毕
autoUpdater.on('update-downloaded', () => {
log.info('Update downloaded');
win.webContents.send('update-downloaded');
});

// 退出并安装
ipcMain.handle('quit-and-install', () => {
try {
autoUpdater.quitAndInstall();
} catch (error) {
log.error('Install failed:', error);
throw error;
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// main.js
const { initAutoUpdater } = require('./autoUpdater');
const createWindow = () => {
mainWindow = new BrowserWindow({
width: 1800,
height: 768,
fullscreen: true,
title: 'update-demo',
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
webviewTag: true, //开启webview标签
}
});
// 隐藏菜单栏
mainWindow.setMenuBarVisibility(false);
mainWindow.setMenu(null)
initAutoUpdater(mainWindow); // 初始化
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
渲染线程
// index.vue
const { ipcRenderer } = window.require('electron');
onMounted(() => {

ipcRenderer.on('update-available', (event, info) => {
updateAvailable.value = true;
isCheckingForUpdate.value = false;
updateInfo.value = info;
});

ipcRenderer.on('update-not-available', () => {
isCheckingForUpdate.value = false;
updateError.value = '当前已是最新版本';
});

ipcRenderer.on('download-progress', (event, progressObj) => {
downloadProgress.value = progressObj.percent.toFixed(1) || 0;
});

ipcRenderer.on('update-downloaded', () => {
isUpdating.value = false;
updateDownloaded.value = true;
});

ipcRenderer.on('update-error', (event, err) => {
updateError.value = err;
isCheckingForUpdate.value = false;
isUpdating.value = false;
});
})

const checkForUpdates = async () => {
try {
if (isCheckingForUpdate.value) {
return;
}
isCheckingForUpdate.value = true;
// console.log(1)
await ipcRenderer.invoke('check-for-updates');
} catch (err) {
updateError.value = '检查更新失败: ' + err;
isCheckingForUpdate.value = false;
}
}

const startUpdate = async () => {
try {
isUpdating.value = true;
await ipcRenderer.invoke('start-download');
} catch (err) {
updateError.value = '开始更新失败: ' + err;
isUpdating.value = false;
}
}

const installUpdate = () => {
ipcRenderer.invoke('quit-and-install');
}

我的微信公众号: 梨的前端小屋


文章作者: 梨啊梨
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 梨啊梨 !
  目录