上周完成了在线更新
的功能,感觉有必要记录一下实现的过程。网上大部分说的都是放在github
或gitlab
上,然后
利用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
文件中修改publish
的url
和version
版本号(否则检测出来永远是最新的)
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
| const { app, ipcMain } = require('electron'); const { autoUpdater } = require('electron-updater');
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
| 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, } }); 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
| 渲染线程
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; 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'); }
|
我的微信公众号: 梨的前端小屋