electron 更新有几种方式,可以看下官网的一些介绍 。这里我们开发的是一个私有的 Electron 应用程序,下面有两种更新方式,一是整个exe更新,第二个是局部热更新,只更新app.asar文件。这里我结合两种方法,先判断有没有exe更新,再判断是否需要热更新
exe更新,使用autoUpdater 实现。参考链接
局部热更新,使用electron-asar-hot-updater 实现,参考链接
1、安装支持包
npm install electron-updater --save
npm install electron-asar-hot-updater -s
2、在main.js实现
import {autoUpdater} from "electron-updater"
const EAU = require('electron-asar-hot-updater');
const isDevelopment = process.env.NODE_ENV !== 'production'
function handleUpdate() {
// 更新前,删除本地安装包 ↓
let updaterCacheDirName = 'xxapp-updater'
const updatePendingPath = path.join(autoUpdater.app.baseCachePath, updaterCacheDirName, 'pending')
fs_extra.emptyDir(updatePendingPath)
// 更新前,删除本地安装包 ↑
log.info("updatePendingPath=" + updatePendingPath);
const returnData = {
error: {status: -1, msg: '检测更新查询异常'},
checking: {status: 0, msg: '正在检查应用程序更新'},
updateAva: {status: 1, msg: '检测到新版本,正在下载,请稍后'},
updateNotAva: {status: -1, msg: '您现在使用的版本为最新版本,无需更新!'},
};
// 本地开发环境,改变app-update.yml地址
if (process.env.NODE_ENV === 'development') {
var dev_path = path.join(__dirname, 'win-unpacked/resources/app-update.yml');
log.info("dev_path=" + dev_path);
autoUpdater.updateConfigPath = dev_path;
// mac的地址是'Contents/Resources/app-update.yml'
}else {
var dev_path = path.join(__dirname, '../app-update.yml');
log.info("dev_path=" + dev_path);
autoUpdater.updateConfigPath = dev_path;
}
//log.info("autoUpdater.updateConfigPath=" + autoUpdater.updateConfigPath);
//和之前package.json配置的一样
autoUpdater.setFeedURL('http://update.tuzhitong.com/download/update');
//autoUpdater.setFeedURL({"headers": "get","url": "http://update.xxx.com/download/update"});
//更新错误
autoUpdater.on('error', function (error) {
sendUpdateMessage(returnData.error)
});
//检查中
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(returnData.checking)
});
//发现新版本
autoUpdater.on('update-available', function (info) {
if(!isDevelopment){
sendUpdateMessage(returnData.updateAva)
}else{
sendUpdateMessage(returnData.updateNotAva)
}
});
//当前版本为最新版本
autoUpdater.on('update-not-available', function (info) {
setTimeout(function () {
hotUpdate();//热更新检查
sendUpdateMessage(returnData.updateNotAva)
}, 1000);
});
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
win.webContents.send('updateAppProgress', progressObj)
});
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
// win.webContents.send('isUpdateNow',"isUpdateNow")
});
//执行自动更新检查
//autoUpdater.checkForUpdates();
}
// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(text) {
//通知BrowserWindow页面显示进度条
//win.webContents.send('updateAppMessage', text)
}
ipcMain.on('isUpdateNow', (e, arg) => {
//some code here to handle event
autoUpdater.quitAndInstall(true,true);
});
ipcMain.on("checkForUpdate", (event, data) => {
//log.info('执行自动更新检查!!!');
autoUpdater.checkForUpdates();
});
ipcMain.on('downloadUpdate', () => {
// 下载
autoUpdater.downloadUpdate()
})
handleUpdate();
function hotUpdate(){
EAU.init({
'api':'http://127.0.0.1:3176/api/apitest/postElectronUpdate', // The API EAU will talk to
'server': false // Where to check. true: server side, false: client side, default: true.
});
EAU.check(function (error, last, body) {
if (error) {
if (error === 'no_update_available') { return false; }
//dialog.showErrorBox('info', error)
return false
}
win.webContents.send('beginUpdate')
EAU.progress(function (state) {
win.webContents.send('updateAppProgress', {percent:state.percent * 100} )
// The state is an object that looks like this:
// {
// percent: 0.5,
// speed: 554732,
// size: {
// total: 90044871,
// transferred: 27610959
// },
// time: {
// elapsed: 36.235,
// remaining: 81.403
// }
// }
if(state.percent >=1){
//dialog.showMessageBox('info', 'App updated successfully! Restart it please.')
}
})
EAU.download(function (error) {
if (error) {
// dialog.showErrorBox('info', error)
return false
}
//通知页面显示进度条
// win.webContents.send('updateAppProgress',{percent: 100} )
setTimeout(() => {
if (process.platform === 'darwin') {
app.relaunch()
app.quit()
} else {
app.quit()
}
}, 2000);
})
})
}
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<处理更新操作 end
3、页面相关监听
<template>
<el-dialog
:title="UpdateInfo.title"
:visible.sync="UpdateInfo.show"
:show-close="false"
:close-on-press-escape="false"
:close-on-click-modal="false"
center
width="50%"
top="45vh">
<div class="conten">
<el-progress :percentage="UpdateInfo.percentage" :color="UpdateInfo.colors" :status="UpdateInfo.progressStaus"></el-progress>
</div>
</el-dialog>
</template>
<script>
export default {
data() {
return {
UpdateInfo:{show:false,percentage: 0,title:"下载进度",
progressStaus :null,colors: [ { color: "#f56c6c", percentage: 20 }, { color: "#e6a23c", percentage: 40 },
{ color: "#6f7ad3", percentage: 60 }, { color: "#1989fa", percentage: 80 }, { color: "#5cb87a", percentage: 100 }
]},
}
},
created: function () {
},
mounted: function () {
this.checkForUpdate();
/**
* 主进程返回的检测状态
*/
this.$ipcRenderer.on('updateAppMessage', (event, data) => {
console.log("updateAppMessage" + data);
switch (data.status) {
case -1:
console.log("updateAppMessage-error:" + data.msg);
//this.$Message.error(data.msg);
break;
case 0:
console.log("updateAppMessage-loading:" + data.msg);
//this.$Message.loading(data.msg);
break;
case 1:
_this.updateApp();
break;
}
});
},
methods: {
downloadUpdate () {
var _this = this;
var _isHotUpdate = false;
this.$ipcRenderer.on('beginUpdate', () => {
_isHotUpdate = true;
_this.UpdateInfo.show = true;
});
//更新进度
this.$ipcRenderer.on('updateAppProgress', (event, data) => {
_this.UpdateInfo.percentage = data.percent.toFixed(0);//(data.percent).toFixed(2);
console.log("data.percent =" + data.percent);
if (data.percent >= 100) {
console.log("isUpdateNow =");
_this.UpdateInfo.title = "下载完成,即将自动重启应用";
if(_isHotUpdate){
//_this.UpdateInfo.show = false;
}else{
setTimeout(() => {
_this.$ipcRenderer.send('isUpdateNow')
}, 1000);
}
}
});
},
updateApp () {
var _this = this;
_this.$confirm('版本有更新,是否立即更新!').then(()=> {
_this.UpdateInfo.show = true;
}).catch(() => {
})
},
checkForUpdate () {
this.$ipcRenderer.send('checkForUpdate', 'a')
this.downloadUpdate();
},
}
}
</script>
4、autoUpdater实现exe更新的时候需要再服务器上部署个人站点,需要以下几个文件
---latest.yml (高版本bulider的时候生成)
---xxx_1.0.0.exe.blockmap(当前版本bulider生成)
---xxx_1.3.0.exe(高版本bulider生成)
---xxx_1.3.0.exe.blockmap(高版本bulider生成)
主要如果是放在iis上,需要添加mime (.yml application/octet-stream )
5、electron-asar-hot-update 进行局部更新的时候需要访问一个api获取是否需要更新,如
[HttpPost]
public dynamic postElectronUpdate()
{
return new
{
name = "xxx",
version = "1.4.0",
asar = "http://127.0.0.1:2046/download/update/update.zip",
info = "test"
};
}
此处的update.zip是打包路径下 win-unpacked\resources\app.asar 这个文件重命名为update.asar文件,然后压缩为zip文件
常见错误问题
1、验证更新时请不要在开发状态验证,不要在开发状态验证,不要在开发状态验证,会有一系列的问题,如MD5不匹配等
2、autoUpdater的服务端文件不要少,不然会报缺失文件
3、electron-asar-hot-update 进行局部更新的时候如果提示 “update.asar中不存在electron-asar-hot-update /update.exe” ,说明asar打包的时候没有把electron-asar-hot-update 这个node_modules打包进去。这个时候需要再配置里打包进去
pluginOptions: {
electronBuilder: {
externals:['electron-asar-hot-updater',....],
...
}
}