17

困扰多日的electron在windows系统下自动更新的问题,终于得到解决,以下是填坑过程。
此教程仅适用于以下条件,使用条件外的,例如使用别的打包方式,未必适用此教程,慎重!


1、系统:windows7 64位操作系统
2、使用electron-boilerplate-vue脚手架,其中electron为v1.2.1,vue为v1.0.25
3、使用electron-packager和grunt-electron-installer打包成安装文件,以及 Squirrel.Windows
4、请先安装vs2015,并配置系统变量,保证Squirrel.Windows的安装环境


教程开始

注:此文不适合electron新手,需要对electron有一定的了解,需清楚electron主进程、渲染进程之间的关系,需清楚electron怎么打包。

(以打包成windows64位安装包并自动更新为例)

------------->第一步:
首先确保你的项目已安装electron-packager,因为我们要使用electron-packager进行第一步的打包,若未安装请安装,并在项目根目录的package.json的script下有类似这条的命令,命令里要有版本号,具体命令书写要求请阅读electron-packager的说明。
参见下图橘色标注的
图片描述

注意 package.json 的额外字段 —— productName、author 和 description,虽然这几个字段并不是打包必备的,但它们会在Squirrel.Windows中使用到。
满足以上要求后,在GitBash中执行 cnpm run-script packager64

图片描述

执行完成后,会在项目根目录下生成一个文件夹OutApp(我在命令中要求的),文件夹里面的文件名称为Client-win32-x64(也是我在命令中要求的),双击文件Client.exe,如果可以正常运行,说明第一步的打包没有问题。

图片描述

------------->第二步:
安装electron-squirrel-startup
npm install electron-squirrel-startup
安装grunt-electron-installer
npm install -g grunt-cli
npm install grunt grunt-electron-installer --save-dev
安装后,在项目根目录下建Gruntfile.js和gruntPackage.json,包含的代码分别为
Gruntfile.js:

var grunt=require('grunt');

//配置
grunt.config.init({
    pkg: grunt.file.readJSON('gruntPackage.json'),
    'create-windows-installer': {
        x64:{
            version:'1.0.0',
            authors:'JXB-XL',
            projectUrl:'',
            appDirectory:'./OutApp/Client-win32-x64',//要打包的输入目录
            outputDirectory:'./OutPut',//grunt打包后的输出目录
            exe:'Client.exe',
            description:'Client',
            setupIcon:"./app/assets/icon/jxb.ico",
            noMsi:true
        }
    }
});

//加载任务
grunt.loadNpmTasks('grunt-electron-installer');

//设置为默认
grunt.registerTask('default', ['create-windows-installer']);

配置内容详见grunt的官方文档

gruntPackage.json:

{
  "name": "Client",
  "version": "1.0.0",
  "devDependencies": {
    "grunt": "^1.2.0",
    "grunt-electron-installer": "^2.1.0"    
  }
}

------------->第三步:
在你的主进程的js中添加以下代码,并调用函数

const electron = require('electron')
//自动更新
const autoUpdater = electron.autoUpdater
function startupEventHandle(){
  if(require('electron-squirrel-startup')) return;
  var handleStartupEvent = function () {
    if (process.platform !== 'win32') {
      return false;
    }
    var squirrelCommand = process.argv[1];
    switch (squirrelCommand) {
      case '--squirrel-install':
      case '--squirrel-updated':
        install();
        return true;
      case '--squirrel-uninstall':
        uninstall();
        app.quit();
        return true;
      case '--squirrel-obsolete':
        app.quit();
        return true;
    }
      // 安装
    function install() {
      var cp = require('child_process');    
      var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe');
      var target = path.basename(process.execPath);
      var child = cp.spawn(updateDotExe, ["--createShortcut", target], { detached: true });
      child.on('close', function(code) {
          app.quit();
      });
    }
    // 卸载
    function uninstall() {
      var cp = require('child_process');    
      var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe');
      var target = path.basename(process.execPath);
      var child = cp.spawn(updateDotExe, ["--removeShortcut", target], { detached: true });
      child.on('close', function(code) {
          app.quit();
      });
    }
  };
  if (handleStartupEvent()) {
    return ;
  }
}
function updateHandle(){
  ipc.on('check-for-update', function(event, arg) {
    let appName='400电话系统';
    let appIcon=__dirname + '/assets/jxb.ico';
    let message={
      error:'检查更新出错',
      checking:'正在检查更新……',
      updateAva:'下载更新成功',
      updateNotAva:'现在使用的就是最新版本,不用更新',
      downloaded:'最新版本已下载,将在重启程序后更新'
    };
    const os = require('os');
    const {dialog} = require('electron');
    autoUpdater.setFeedURL('放最新版本文件的文件夹的服务器地址');
    autoUpdater.on('error', function(error){
      return dialog.showMessageBox(mainWindow, {
          type: 'info',
          icon: appIcon,
          buttons: ['OK'],
          title: appName,
          message: message.error,
          detail: '\r'+error
      });
    })
    .on('checking-for-update', function(e) {
        return dialog.showMessageBox(mainWindow, {
          type: 'info',
          icon: appIcon,
          buttons: ['OK'],
          title: appName,
          message: message.checking
      });
    })
    .on('update-available', function(e) {
        var downloadConfirmation = dialog.showMessageBox(mainWindow, {
            type: 'info',
            icon: appIcon,
            buttons: ['OK'],
            title: appName,
            message: message.updateAva
        });
        if (downloadConfirmation === 0) {
            return;
        }
    })
    .on('update-not-available', function(e) {
        return dialog.showMessageBox(mainWindow, {
            type: 'info',
            icon: appIcon,
            buttons: ['OK'],
            title: appName,
            message: message.updateNotAva
        });
    })
    .on('update-downloaded',  function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
        var index = dialog.showMessageBox(mainWindow, {
            type: 'info',
            icon: appIcon,
            buttons: ['现在重启','稍后重启'],
            title: appName,
            message: message.downloaded,
            detail: releaseName + "\n\n" + releaseNotes
        });
        if (index === 1) return;
        autoUpdater.quitAndInstall();
    });
    autoUpdater.checkForUpdates();
 });
}

------------->第四步:
执行grunt,在你指定的目录生成三个文件,exe文件是安装包,RELEASES包含安装及版本信息,nupkg文件目前还没搞懂。
图片描述

到目前为止,1.0.0版本的安装文件已经搞定,双击exe文件后,弹出绿色的安装动画,无法选择安装目录,会自动安装在C:\Users\Administrator\AppData\Local下,安装结束后动画消失,并自动创建了快捷方式
图片描述

------------->第五步:
到目前为止,低版本的桌面应用已打包并安装完毕,我把版本号写在了页面上,可以看到是1.0.0版本的
图片描述

那么怎么更新到新的版本呢?
方法如下:
1、修改版本信息,包括Gruntfile.js、gruntPackage.json、package.json下面所有的版本信息,改为1.0.1,然后从第一步开始执行,直到第四步,获得1.0.1版本的三个文件(release、exe、nupkg)后,将这三个文件上传到放最新版本文件的文件夹的服务器地址,对的,这个就是对应的主进程js中的autoUpdater.setFeedURL(第三步中)。
2、重启1.0.0桌面应用,点击检查更新(这里,我使用了ipc,即渲染进程向主进程发送消息,主进程监听'check-for-update',即第三步中的ipc.on('check-for-update', function(event, arg) {}))。
3、分别弹出以下窗口:
图片描述

图片描述

图片描述

点击现在重启,就会自动关闭1.0.0版本的,并打开1.0.1版本的,打开后发现,已经更新成功

图片描述

再点击检查更新,会弹出

图片描述

再打开安装目录,会发现多了一个1.0.1的文件夹
图片描述

你可以在自动更新成功后,用node将之前的1.0.0文件夹删除。
自动更新结束!

原理

引用自https://github.com/Squirrel/S...
1、根据设置的setFeedURL路径,UpdateManager检查路径下的RELEASES文件,与安装目录下的RELEASES文件进行比对。
2、如果出现版本不同的情况,将下载路径中的更新包,下载完成后自动进行更新(包括修改快捷方式和清除旧版本)。
3、更新成功后,会保留上一个版本,并清除上一个版本之前的所有版本。

参考文章

从零开始使用Electron + jQuery开发桌面应用 (二) 打包应用https://segmentfault.com/a/11...
【译】Electron 自动更新的完整教程(Windows 和 OSX)https://segmentfault.com/a/11...

1、开发阶段就进行检查更新,报错:Cannot find squirrel,这是因为此桌面应用没有安装,必须在安装后才能执行检查更新

2、桌面应用安装成功后,点击检查更新,报错:another instance is exist,这是因为首次安装后,一些安装文件还没有退出,所以尽量在安装完毕的一分钟后再检查更新。自动更新后不会有这个问题,仅发生在首次安装后。

3、你的exe文件名不能包含数字,否则也会报错。

4、一些同学说,安装后检查更新报错:Cannot find module ‘electron-squirrel-startup’,应该是没有安装Squirrel.Windows的原因,请先安装Squirrel.Windows,需要vs2015的环境(vs2015需要下载并安装,安装会占用C盘几个G的空间,请先保证C盘有足够空间。安装后请设置系统变量,即在Path下添加C:\Program Files (x86)\MSBuild\14.0\Bin)

5、更换了办公的电脑,系统变成win10,重新下载安装环境后发现,如果现有的项目版本高于服务器上的项目版本,就会自动更新失败,C盘的安装目录下的squirrel文件显示'local version is greater than remote version'


七王国
326 声望39 粉丝