之前介绍过在electron-builder打包基础下怎么使用electron-updater实现应用自动监测版本以及更新的,现在来记录一下怎么实现 热更新
electron应用热更新具体实现流程图
正常执行electron-builder进行打包,打包完成后将win-ia32-unpacked\resources下的文件压缩成latest.zip文件(注意:压缩时需要选中三个文件压缩,而非压缩三个文件的上级文件夹,推荐使用mac电脑压缩),上传到服务器;客户端每次重启执行一次检测更新,比较本地与远程服务器的版本,如有最新版本,则下载zip包,通过Node fs模块写入本地,解压覆盖到本地resources下文件,重启app完成更新;
electron应用热更新具体实现步骤
1、 在渲染进程引入ipcRenderer,监听从主进程传过来的更新事件,应用启动后在渲染进程调用接口比对服务器最新资源版本和本地资源版本是否一致,若不一致则向主进程触发资源热更新函数。
ipcRenderer.send('upgrading', url + 'latest.zip') // 用来触发热更新函数
2、 在主进程模块下添加 upgrade.js主要处理更新资源的下载和替换,并将资源更新进度发送给渲染进程进行渲染,让进度可视化。
const path = require('path')
const http = require('http')
const fs = require('fs')
const dir = path.resolve(__dirname, '../')
var AdmZip = require('adm-zip')
const { beforeWriteAppLog } = require('./export.js')
const upgradeFn = function (appResourcesUrl) {
var destUrl = `${dir}/latest.zip`
downloadFile(appResourcesUrl, destUrl, (state, data) => {
if (state === 'progress') {
global.mainWindow.webContents.send('hotDownloadProgress', data) // 通知渲染进程更新资源下载进度
} else if (state === 'Download completed') {
} else if (state === 'finish') {
try {
var zip = new AdmZip(destUrl)
zip.getEntries()
zip.extractAllTo(dir, true)
deleteFile(destUrl)
global.mainWindow.webContents.send('isUpdateHotNow') // 通知渲染进程更新资源下载完成
} catch (err) {
errorFn('AdmZip', err)
global.mainWindow.webContents.send('hot-updata-error') // 通知渲染进程更新资源下载错误
}
} else if (state === 'error') {
errorFn('hot-updata-downloadFile', 'stream error' + data)
global.mainWindow.webContents.send('hot-updata-error')
}
})
}
/*
* url 网络文件地址
* dest 文件存储位置
* cb 回调函数
*/
const downloadFile = (url, dest, cb = () => {} ) => {
const stream = fs.createWriteStream(dest)
http.get(url, (res) => {
if (res.statusCode !== 200) {
cb('error', 'response.statusCode error:' + res.statusCode)
return
}
// 进度
const len = parseInt(res.headers['content-length']) // 文件总长度
let cur = 0
const total = (len / 1048576).toFixed(2) // 转为M 1048576 - bytes in 1Megabyte
res.on('data', function (chunk) {
cur += chunk.length
const progress = (100.0 * cur / len).toFixed(2) // 当前下载进度百分比
const currProgress = (cur / 1048576).toFixed(2) // 当前下载大小
console.log(progress, currProgress, total)
cb('progress', progress)
})
res.on('end', () => {
cb('Download completed')
})
// 超时,结束等
stream.on('finish', () => {
stream.close(cb('finish'))
}).on('error', (err) => {
deleteFile(dest)
if (cb) cb('error', 'stream error:' + err.message)
})
res.pipe(stream)
})
}
/*
* 删除文件
*/
function deleteFile (url) {
fs.unlink(url, function (err) {
if (err) {
errorFn('deleteFile', JSON.stringify(err))
}
})
}
3、 在主进程main.js文件中引入 ipcMain,监听资源热更新函数upgrading,触发upgrade.js中的upgradeFn函数
// 监听热更新
ipcMain.on('upgrading', (evt, url) => {
upgradeFn(url)
})
4、在渲染进程中添加热更新事件的监听,获取资源下载进度,在这一步实现可视化效果
if (isElectron()) {
// 热更新下载进度
ipcRenderer.on('hotDownloadProgress', (event, percent) => {
})
// 资源替换完成,重启app完成更新
ipcRenderer.on('isUpdateHotNow', (event) => {
ipcRenderer.send('appRelaunch')
})
// 热更新失败
ipcRenderer.on('hot-updata-error', (event) => {
})
}