当前位置: 首页 > news >正文

[electron]升级功能

前言

electron应用打包后,希望可以实现自动嗅探新版本,下载新版本并覆盖安装。

准备工作

项目中使用的技术栈:electron + react,打包工具为electron-builder,升级工具为electron-updater。项目中使用了框架electron-prokit,其中封装了ipc的过程。
需要一个放置软件包的存储服务器,这边使用了Amazon S3,最好安装桌面工具S3 Brower,简化开发过程。
对于S3,公司内部会有自己的Endpoint,并配置AK和SK。配置好后创建Bucket,确认软件包的存放路径。
例如Endpoint是abc.com,Bucket名称为test,那么首先需要把test的Bucket Policy修改为

{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": "*","Action": "s3:GetObject","Resource": "arn:aws:s3:::test/*"}]
}

并把Permissions尽量放开。(感觉这步非必须,还需要试验一下)

配置electron-builder

electron-builder打包时需要生成last.yml,将这个文件上传到存储服务器后,在本地安装的软件通过读取这个文件来获取是否有新的版本。

在electron-builder中需要配置publish的选项,配置之后才会生成last.yml。同时在根目录创建release-notes.md,每次更新时可以把更新内容写在里边,这样也可以在last.yml中读取到。

    "publish": {"provider": "generic","url": "https://'https://abc.com/test/path/'    //endpoint名称/bucket名称/存储路径},"releaseInfo": {"releaseNotesFile": "release-notes.md"}

主进程代码编写

在electron的主进程文件夹中创建一个update.js,用来注册更新过程。

import { Notification, BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
import { sendMsgToRender } from 'electron-prokit';export async function initUpdate(mainWindow: BrowserWindow) {autoUpdater.forceDevUpdateConfig = true;autoUpdater.disableWebInstaller = false;autoUpdater.autoDownload = false;autoUpdater.setFeedURL('https://abc.com/test/path/') //endpoint名称/bucket名称/存储路径autoUpdater.checkForUpdates();autoUpdater.on('update-available', (info) => {sendMsgToRender('main', { key: 'update-available', info });});autoUpdater.on('update-not-available', () => {new Notification({ title: '无更新可用', body: '无更新可用' }).show();});autoUpdater.on('download-progress', (progressObj) => {sendMsgToRender('main', { key: 'download-progress', progressObj });});autoUpdater.on('update-downloaded', () => {sendMsgToRender('main', 'update-downloaded');});autoUpdater.on('error', (error) => {new Notification({ title: '更新错误', body: `更新失败: ${error}` }).show();sendMsgToRender('main', { key: 'update-error', error });});
}export function checkUpdate() {autoUpdater.checkForUpdates();
}export async function downloadUpdate() {await autoUpdater.downloadUpdate();
}export function installUpdate() {autoUpdater.quitAndInstall();
}

在创建窗口后调用initUpdate()

app.whenReady().then(() => {// 创建窗口createWindow();// 检查更新initUpdate()();}).catch(console.log);

创建窗口后的initUpdate中首先去check有无新版本,如果没有新版本,则会弹出Notification,如果有新版本,会给主窗口发ipc消息,key为update-available
并在ipc的注册函数中进行downloadUpdate和installUpdate的注册,以便接受渲染进程传进来的消息。

渲染进程

在渲染进程中需要创建一个更新的弹窗,这里使用react配合antd。

import React, { useState, useEffect, useImperativeHandle } from 'react';
import { onMsgFromMain, sendMsgToMain } from 'electron-prokit';
import { Modal, Flex, Space, Progress, Button } from 'antd';
import Markdown from 'react-markdown';
import { t } from 'i18next';const Index = React.forwardRef((props, ref) => {useImperativeHandle(ref, () => ({showModalHandle,}));const [openModal, setOpenModal] = useState(false);const [newVersion, setNewVersion] = useState('');const [releaseNote, setReleaseNote] = useState('');const [state, setState] = React.useState('');const [downloadPercent, setDownloadPercent] = React.useState(0);const showModalHandle = (info: any) => {setOpenModal(true);setState('update-available');const { version = '', releaseNotes = '' } = info;setNewVersion(version);setReleaseNote(releaseNotes);};const handleCancel = () => {setOpenModal(false);};const handleDownload = () => {sendMsgToMain({key: 'downloadUpdate'});};const handleInstall = () => {sendMsgToMain({key: 'newInstall'});};useEffect(() => {onMsgFromMain((event, args) => {if (args.key === 'download-progress') {setState('download-progress');setDownloadPercent(args.progressObj.percent.toFixed(2));}if (args === 'update-downloaded') {setState('update-downloaded');}if (args.key === 'update-error') {setState('update-error');console.error('update-error', args.error)setTimeout(() => {setOpenModal(false);}, 10000);}});});const footer = (<>{state === 'download-progress' && null}{state === 'update-unavailable' && null}{state === 'update-available' &&<><Buttontype="primary"onClick={handleDownload}>{t('update')}</Button><Button onClick={handleCancel}>{t('Update later')}</Button></>}{state === 'update-downloaded' &&<><Buttontype="primary"onClick={handleInstall}>{t('update')}</Button><Button onClick={handleCancel}>{t('close')}</Button></>}{state === 'update-error' &&<Button onClick={handleCancel}>{t('close')}</Button>}</>);return (<Modalwidth={400}open={openModal}title={`${t('New version is available')}: ${newVersion}`}onCancel={handleCancel}closable={false}footer={footer}maskClosable={false}><Flex style={{ height: 150 }}><Space direction="vertical" style={{ width: '100%', padding: '0px 20px', overflow: 'auto' }}>{state === 'update-available' && <Markdown>{releaseNote}</Markdown>}{state === 'download-progress' && <Flex justify={'center'} align={'center'}><Progress type="circle" percent={downloadPercent} /></Flex>}{state === 'update-unavailable' && <div>{t('No new version available')}</div>}{state === 'update-downloaded' && <div>{t('The latest version has been downloaded. Do you want to update it')}</div>}{state === 'update-error' && <div dangerouslySetInnerHTML={{ __html: t('Update error') }} />}</Space></Flex></Modal>);
});export default Index;

在根组件中引用此Modal,并接收key为update-available时showModal。

import UpdateModal from './UpdateModal'
...const updateRef = useRef(null);useEffect(() => {onMsgFromMain((_event: unknown, args: any) => {if (args.key === 'update-available') {updateRef.current.showModalHandle(args.info);}});}
...
<UpdateModal ref={updateRef}></UpdateModal>

升级功能基本实现。

其他

Notification

在electron自带的Notification中,想要设置展示头,可以在app注册时设置

app.setAppUserModelId('Test')

覆盖安装

有可能是起项目时候有什么问题(很老的项目),发现下载下来的安装包不能覆盖安装,报软件无法关闭的错误。
这里可以配置nsis

    "nsis": {"oneClick": false,"perMachine": false,"createDesktopShortcut": true,"createStartMenuShortcut": true,"guid": "Test","include": "build/install/test.nsh","shortcutName": "Test","artifactName": "Test Setup ${version}.${ext}","allowElevation": false,"allowToChangeInstallationDirectory": true,"deleteAppDataOnUninstall": true,"runAfterFinish": false},

其中test.nsh中添加:

!macro customInit
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Test"
!macroend

在每次安装之前把已安装版本卸载,即可解决这个问题。

http://www.dtcms.com/a/275251.html

相关文章:

  • CSS Grid布局和Flexbox有什么区别?
  • C语言文件读写操作详解:fgetc与feof函数的应用
  • 经典同步问题详解
  • 使用 lstrip() 和 rstrip() 方法
  • java集合类
  • 【牛客刷题】吃糖果----糖果甜度问题(贪心策略详解)
  • 机器学习详解
  • Windows删除文件或者拔出U盘显示正在使用/占用解决办法
  • Android tombstones memory map分析
  • HarmonyOS从入门到精通:动画设计与实现之四 - 转场动画设计与流畅交互体验
  • 优选算法 --(双指针算法 1~8)
  • The Practice of Programming
  • 深入解码 Docker 镜像与容器的奇妙世界
  • 小车循迹功能的实现(第六天)
  • 自由学习记录(68)
  • C#事件:从原理到实践的深度剖析
  • 数据结构 顺序表(3)---顺序表的应用
  • 网安学习NO.14
  • 创意总监的动态视觉秘诀:用AE动态遮罩AI,轻松实现“人景分离”
  • 分割网络Segformer
  • 需求跟踪深度解析:架构师视角下的全链路追溯体系
  • Vue性能监控
  • PreparedStatement 实现分页查询详解
  • 你以为大数据只是存?其实真正的“宝藏”藏在这招里——数据挖掘!
  • 自动评论+AI 写作+定时发布,这款媒体工具让自媒体人躺赚流量
  • 卸载软件总留一堆“垃圾”?这款免费神器,一键扫清注册表和文件残留!
  • BLOB 数据的插入与读取详解
  • 9月22日跨境电商高峰会都说了啥?郑州跨境电商发展机遇在哪?
  • Nginx的配置与使用
  • 多元思维模型:数据分析需要具备的四大能力?