2

Electron增量更新(三)

 2 years ago
source link: https://segmentfault.com/a/1190000041145271
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

写Electron这个系列以来,还是有不少同学问了一些问题的,主要是针对于electron增量更新的,之前我写过增量更新(二),但是其Windows方式需要管理员权限包,不太优雅,且如果想要拖拽功能的话,由于权限问题,提权后的软件会禁止拖动(将外部文件拖入软件窗口会有一个禁止图标),那么这一期我会对增量更新进行一次比较详细的说明以及提供临时提权更新的方案供大家参考。

Mac更新

有些同学问了Mac平台下的系统如何增量更新的,实际上Mac下的Electron的asar文件是可以直接替换的,故下载替换后,开个定时器重启就行了。

Windows

其实我们的桌面软件大都不需要管理员权限的(就是软件上带盾牌的),我们发现很多软件都是在需要更新时会有一个弹窗出现,你点击后会对其进行临时提权进行更新,本期的目标就是实现这种临时提权的功能进行增量更新。

其实我们增量更新有两个难点,要想实现得攻克这两个主要问题:

  1. 什么时候需要uac提权?
  2. 怎么处理Electron应用启动后app.asar被占用锁定的问题?
  1. Windows系统是有不同的用户权限级别的,比如我是普通用户在修改c盘某些文件夹的文件时,会有一个弹窗让你使用管理员权限创建或修改等等,当软件被安装在c盘时,我们就需要管理员权限来替换app.asar文件。exe
  2. 关于锁定问题,在增量二中已经说明了一下,处理方式就是关闭我们的Electron应用,然后再进行app.asar文件的替换。
  1. 编写bat脚本,在脚本中执行Electron应用的关闭,app.asar文件的替换,重启Electron应用,然后将bat脚本打包成exe文件。
  2. 下载update.asar(更新版本的app.asar),判断用户软件是否安装在c盘,是:使用sudo-prompt这个包临时提权执行上面的exe,不是:可以不用提权,直接使用node的exec执行上面的exe。

模拟请求什么的前两期更新已经说了,点击这里增量更新(二),看看步骤,这里就不再多做说明。

构建exe

@echo off
taskkill /f /im %3
timeout /T 1 /NOBREAK
del /f /q /a %1\app.asar
move %2\update.asar %1
ren %1\update.asar app.asar
explorer.exe %4

简单解释一下吧,%1和%2为运行脚本传入的参数,比如update.bat aaa bbb,那么%1为aaa,%2为bbb,上面我们执行exe时传入的,
也就是%1为resourcesPath(也就是我们的app.asar所在目录),%2为下载更新的update.asar所在目录,%3为软件的进程名称(可在任务管理器中查看),%4为软件的启动exe。
这里的逻辑是Electron应用,暂停1秒钟,然后删除app.asar,将update.asar移动到app.asar目录下,重命名为app.asar,启动Electron应用。
下载Bat To Exe Converter这个软件,将update.bat转换为update.exe,详细操作请看前面两期的详细说明。

对比上一期的我们发现启动时没用start命令了,而是用的explorer.exe,它是Windows系统自带的程序管理器,这里的处理是打包的Electron应用不带有管理员权限,但是如果我们提权后执行此exe启动Electron应用,我们会发现此时Electron应用也具有了管理员权限,故需要使用explorer.exe降权启动。

sudo-prompt

安装sudo-prompt,npm i sudo-prompt,sudo-prompt这个包和node的exec命令差不多,执行时会有一个提权弹窗出现,用户确认后会以管理员权限执行命令,新建sudoPrompt.js:

var sudo = require('sudo-prompt')
var options = {
  name: 'Electron',
}

export default (shell) => {
  return new Promise((resolve, reject) => {
    sudo.exec(shell, options, function(error, stdout, stderr) {
      if (error) {
        reject(error)
        console.log('error:' + error)
        return
      }
      resolve(stdout)
      console.log('stdout: ' + stdout)
    })
  })
}
// npm i https://github.com/xuxingeren/sudo-prompt
"sudo-prompt": "git+https://github.com/xuxingeren/sudo-prompt.git",

注意:sudo-prompt这个包很久没维护了,现在最新的还有一些问题,主要是带有中文路径会出错,原作者已经关闭了提不了pr,我这边处理了一下,使用的话可以fork我的分支来进行构建,地址

主进程更新处理

更新前的准备以及渲染进程处理这里就略过了,请见前面两期内容

import downloadFile from './downloadFile'
import sudoPrompt from './sudoPrompt'
import { app } from 'electron'
const fse = require('fs-extra')
const path = require('path')
const AdmZip = require('adm-zip')
import log from '../config/log.js'

export default async (data) => {
  const resourcesPath = process.resourcesPath
  // 下载我们上面打包好的更新exe,我这里是放在app.getPath('userData')下的,其他位置也可
  if (!fse.pathExistsSync(path.join(app.getPath('userData'), './update.exe'))) {
    await downloadFile({
      url: data.upDateExe,
      targetPath: app.getPath('userData')
    }).catch((err) => {
      console.log(err)
      log.info(err)
    })
  }
  // 提权的方案,这里简写了
  const downloads = app.getPath('downloads')
  // 下载update.asar,解压(其实不用压缩也可)在系统的下载目录
  downloadFile({ url: data.upDateUrl, targetPath: downloads })
    .then(async (filePath) => {
      const zip = new AdmZip(filePath)
      zip.extractAllToAsync(downloads, true, (err) => {
        if (err) {
          console.error(err)
          return
        }
        fse.removeSync(filePath)
        // 这里可以添加判断,如果软件是安装在c盘使用sudoPrompt进行提权执行update.exe,不是的话可以直接执行update.exe
        // 临时提权运行exe,exe中关闭主进程,替换安装c盘中的asar(提权是为了处理c盘,如果安装其他盘,可以直接用node.exec运行exe替换)
        // 由于提权后的exe打开electron,导致其启动后也会是管理员权限,故需降权进行启动,explorer.exe
        sudoPrompt(
          `"${path.join(
            app.getPath('userData'),
            './update.exe'
          )}" "${resourcesPath}" ${downloads} "${
            process.env.VUE_APP_PRODUCTNAME
          }.exe" "${app.getPath('exe')}"`
        )
      })
    })
    .catch((err) => {
      log.info(err)
      console.log(err)
    })
}

手动调试的话你可以在cmd中执行update.exe xxx xxx xxx xxx传入对应更新的地址看执行是否成功。

本系列更新只有利用周末和下班时间整理,比较多的内容的话更新会比较慢,希望能对你有所帮助,请多多star或点赞收藏支持一下

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK