更新-增量更新(二)

不好意思,鸽了挺久了,上一期咱们应用app.asar.unpacked实现了electron的增量更新,本期咱们介绍的是如何应用exe替换asar来实现增量更新。
本期内容是基于上一期的内容来解说的,且次要针对于Windows零碎(mac零碎能够对app.asar批改)

替换难点

  1. 在应用了asar后,Windows零碎的Electron利用启动后其app.asar会被占用,不能对其进行批改和删除,必须完结Electron利用的过程后才能够对其进行批改。
  2. 因为Windows的uac限度,如果装置在C盘的话,批改app.asar会有权限问题。

解决思路

其实呢子线程也能够跑node,然而呢因为主过程完结了咱们并没有node环境运行node命令,所以此办法不通,当然你也可增加一个编译好的node运行子线程js,然而体积问题得失相当。
在Windows下咱们能够用批处理文件bat来解决文件,然而bat还是有uac以及执行时会有cmd窗口,咱们能够把写好的bat文件转换为exe文件来解决这些问题。

  1. 咱们能够应用node衍生独立存在的子过程,把子过程和主过程分来到,完结掉Electron利用的过程,用这个子过程进行替换。
  2. uac限度能够应用exe文件来获取管理员权限进行解决。

解决方案

1. 打包批改

咱们这里去除之前的app.asar.unpacked打包,把之前vue.config的正文掉,这样咱们打包就只有asar文件了

// 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"// ],

2. 构建增量zip

上一期构建增量zip时应用了afterPack钩子,这里咱们对其批改,把新版本的app.asar重命名为update.asar,放入增量的app.zip包里,不理解的能够看看上一期的内容

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'))}

3. 模仿接口

同上一期,咱们这里批改了upDateUrl和upDateExe,upDateUrl是增量zip,upDateExe是咱们用来替换asar的exe文件。

{  "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"  }}

4. 加载策略批改

这里咱们再把加载策略批改为加载app.asar里的文件

// createProtocol('app', path.join(resources, './app.asar.unpacked'))createProtocol('app')

5. 渲染过程增量更新

这个没变动,同上一期

6. 主过程解决

咱们把之前的主过程下载批改一下,先判断update.exe是否存在,不存在的话先下载update.exe放入app.getPath('userData')里:

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)敞开主过程

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)    })}

7. 子过程解决

主过程敞开后会触发quit事件,咱们在这个事件里检测update.exe以及update.asar是否同时存在,
同时存在的话咱们用spawn开启一个子过程运行咱们的update.exe,并且传入resourcesPath(app.asar所在目录门路),app.getPath('exe')(咱们软件的启动门路),
应用child.unref()让子过程和父过程拆散,能够不退出子过程的状况下退出父过程。

const { spawn } = require('child_process')const fse = require('fs-extra')const fs = require('fs')const resourcesPath = process.resourcesPathapp.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>\logsmac: ~/Library/Logs/<app name> ?应该是这个上面的,这个我没验证

8. 构建exe

筹备工作实现了,这里咱们编写exe,其实这个没啥难度的,咱们应用bat脚本打包成exe就行。
update.bat

@echo offtimeout /T 1 /NOBREAKdel /f /q /a %1\app.asarren %1\update.asar app.asarstart "" %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或点赞珍藏反对一下

本文地址:链接
本文github地址:链接