很差意思,鸽了挺久了,上一期咱们使用app.asar.unpacked
完成了electron的增量更新,本期咱们介绍的是如何使用exe替换asar
来实现增量更新。
本期内容是基于上一期的内容来说解的,且主要针对于Windows系统(mac系统能够对app.asar修改)vue
asar
后,Windows系统的Electron应用启动后其app.asar会被占用,不能对其进行修改和删除,必须结束Electron应用的进程后才能够对其进行修改。其实呢子线程也能够跑node,可是呢因为主进程结束了咱们并无node环境运行node命令,因此此方法不通,固然你也可添加一个编译好的node运行子线程js,可是体积问题得不偿失。
在Windows下咱们能够用批处理文件bat来处理文件,可是bat仍是有uac以及执行时会有cmd窗口,咱们能够把写好的bat文件转换为exe文件来解决这些问题。node
咱们这里去除以前的app.asar.unpacked
打包,把以前vue.config的注释掉,这样咱们打包就只有asar文件了git
// extraResources: [{ // from: "dist_electron/bundled", // to: "app.asar.unpacked", // filter: [ // "!**/icons", // "!**/preload.js", // "!**/node_modules", // "!**/background.js" // ] // }], // files: [ // "**/icons/*", // "**/preload.js", // "**/node_modules/**/*", // "**/background.js" // ],
上一期构建增量zip时使用了afterPack钩子,这里咱们对其修改,把新版本的app.asar重命名为update.asar
,放入增量的app.zip包里,不了解的能够看看上一期的内容github
const path = require('path') const AdmZip = require('adm-zip') const fse = require('fs-extra') exports.default = async function(context) { let targetPath if(context.packager.platform.nodeName === 'darwin') { targetPath = path.join(context.appOutDir, `${context.packager.appInfo.productName}.app/Contents/Resources`) } else { targetPath = path.join(context.appOutDir, './resources') } const asar = path.join(targetPath, './app.asar') fse.copySync(asar, path.join(context.outDir, './update.asar')) var zip = new AdmZip() zip.addLocalFile(path.join(context.outDir, './update.asar')) zip.writeZip(path.join(context.outDir, 'app.zip')) fse.removeSync(path.join(context.outDir, './update.asar')) }
同上一期,咱们这里修改了upDateUrl和upDateExe,upDateUrl是增量zip,upDateExe是咱们用来替换asar的exe文件。vue-cli
{ "code": 200, "success": true, "data": { "forceUpdate": false, "fullUpdate": false, "upDateUrl": "http://127.0.0.1:4000/app.zip", "upDateExe": "http://127.0.0.1:4000/update.exe", "restart": false, "message": "我要升级成0.0.2", "version": "0.0.2" } }
这里咱们再把加载策略修改成加载app.asar里的文件shell
// createProtocol('app', path.join(resources, './app.asar.unpacked')) createProtocol('app')
这个没变更,同上一期app
咱们把以前的主进程下载修改一下,先判断update.exe
是否存在,不存在的话先下载update.exe
放入app.getPath('userData')
里:electron
win:C:\Users\Administrator(你的用户)\AppData\Roaming\<app name>\ mac:/Users/(你的用户)/Library/Application Support/<app name>
app.getPath('userData')
这个路径呢,比较特殊,安装以后就存在了,是数据文件,你的indexDB,localStorage等都存在这里面,软件的全量更新,卸载都不会改动这个文件里的东西,
咱们把update.exe
放入这里,避免全量更新后从新安装。以后下载增量更新包解压到resourcesh中(update.asar),也就是和app.asar同级目录,删除zip包,运行app.exit(0)
关闭主进程async
import downloadFile from './downloadFile' import { app } from 'electron' const fse = require('fs-extra') const path = require('path') const AdmZip = require('adm-zip') export default async (data) => { const resourcesPath = process.resourcesPath if (!fse.pathExistsSync(path.join(app.getPath('userData'), './update.exe'))) { await downloadFile({ url: data.upDateExe, targetPath: app.getPath('userData') }) } downloadFile({ url: data.upDateUrl, targetPath: resourcesPath }).then(async (filePath) => { const zip = new AdmZip(filePath) zip.extractAllToAsync(resourcesPath, true, (err) => { if (err) { console.error(err) return } fse.removeSync(filePath) app.exit(0) }) }).catch(err => { console.log(err) }) }
主进程关闭后会触发quit
事件,咱们在这个事件里检测update.exe
以及update.asar
是否同时存在,
同时存在的话咱们用spawn开启一个子进程运行咱们的update.exe
,而且传入resourcesPath
(app.asar所在目录路径),app.getPath('exe')
(咱们软件的启动路径),
使用child.unref()
让子进程和父进程分离,能够不退出子进程的状况下退出父进程。ui
const { spawn } = require('child_process') const fse = require('fs-extra') const fs = require('fs') const resourcesPath = process.resourcesPath app.on('quit', () => { console.log('quit') if (fse.pathExistsSync(path.join(app.getPath('userData'), './')) && fse.pathExistsSync(path.join(resourcesPath, './update.asar'))) { const logPath = app.getPath('logs') const out = fs.openSync(path.join(logPath, './out.log'), 'a') const err = fs.openSync(path.join(logPath, './err.log'), 'a') const child = spawn(`"${path.join(app.getPath('userData'), './update.exe')}"`, [`"${resourcesPath}"`, `"${app.getPath('exe')}"`], { detached: true, shell: true, stdio: ['ignore', out, err] }) child.unref() } })
也就是说这里是父进程退出后,子进程执行咱们的exe,替换app.asar,out和err是将子进程执行的日志重定向到app.getPath('logs')
中,这个路径和electron-log
不同(你也能够本身设置为electron-log路径同样)
win:C:\Users\Administrator(你的用户)\AppData\Roaming\<app name>\<app productName>\logs mac: ~/Library/Logs/<app name> ?应该是这个下面的,这个我没验证
准备工做完成了,这里咱们编写exe,其实这个没啥难度的,咱们使用bat脚本打包成exe就行。
update.bat
@echo off timeout /T 1 /NOBREAK del /f /q /a %1\app.asar ren %1\update.asar app.asar start "" %2
简单解释一下吧,%1和%2为运行脚本传入的参数,好比update.bat aaa bbb
,那么%1为aaa,%2为bbb,上面咱们用spawn运行exe时传入的,
也就是%1为resourcesPath,%2为软件的启动exe,咱们运行bat脚本,先暂停1秒钟保证主进程退出了,而后删除app.asar,将update.asar重命名为app.asar,启动软件exe。
一个简单的bat替换就完成了,咱们下载Bat To Exe Converter这个软件,将update.bat转换为update.exe,而后将update.exe放入咱们的http-server目录中。运行软件检测更新,看看更新是否完成。
spawn(`"${path.join(app.getPath('userData'), './update.exe')}"`, [`"${resourcesPath}"`, `"${app.getPath('exe')}"`], { detached: true, shell: true, stdio: ['ignore', out, err] })
这里有同窗可能会有疑问,为何要在几个路径外加一个"",这是因为node运行脚本的路径名中包含空格的话,须要加上引号,bat处理也同样,好比咱们的软件安装在c盘,C:\Program Files\electronVueDEV
,最多见的问题就是Program Files
这里有个空格,这会致使bat命令里有这样的路径的话会处理失败,因此咱们的路径都加了引号的。
本系列更新只有利用周末和下班时间整理,比较多的内容的话更新会比较慢,但愿能对你有所帮助,请多多star或点赞收藏支持一下