Electron实现自动更新

1,980 阅读3分钟

在使用Electron开发的时候有一个需求:自动更新应用。

踩了一些坑,记录一下,希望可以帮到大家

我找到了两个方案

1.Electron中文网中的autoUpdater

2.electron.build的electron-uploader

第一种方法的缺点

1.需要使用Squirrel框架,否则会报错Can not find Squirrel,并且打包还有其他要求

我使用的是第二种方法,原因如下

1.electron使用的electron.build进行打包的,那么用electron.build提供的自动更新当日是更好的

2.代码比较简单

话不多说,上代码

首先线安装插件

npm install electron-updater

还使用到了electron的进程通讯

代码如下

自动跟新配置文件upload.js

const { ipcMain } = require('electron')
import { autoUpdater } from "electron-updater";

export default  function handleUpdate(win){
  
  //是否自动下载,默认设置为不自动下载
  autoUpdater.autoDownload = false

  //设置更新路径
  autoUpdater.setFeedURL("http://192.168.0.148:9091")

   // 更新发生错误时触发
   autoUpdater.on('error', (error)=> {
    win.webContents.send('message', "更新出错")
    win.webContents.send('installErr', error)
  })

  //检查更新是否已开始时发出
  autoUpdater.on("checkingForUpdate",()=>{
    win.webContents.send('message', "正在检查更新。。。")
  })

  //检查应用可以更新,不自动下载
  autoUpdater.on("update-available",(e,info)=>{
    win.webContents.send('message', "应用可以更新")
    win.webContents.send('updateAvailable', info)
  })
  

  //检查应用没有新版本
  autoUpdater.on("update-not-available",()=>{
    win.webContents.send('message', "应用没有更新")
  })

  //应用下载完成事件
  autoUpdater.on("update-downloaded",(event)=>{
    win.webContents.send('message', "下载完成事件")
    win.webContents.send('requestInstall', event)
  })

  //应用下载进度条事件
  autoUpdater.on("download-progress",(progressObj)=>{
    win.webContents.send('message', "应用下载中")
    let info = {
      bytesPerSecond: progressObj.bytesPerSecond,
      percent: progressObj.percent,
      transferred: progressObj.transferred,
      total: progressObj.total
    }
    win.webContents.send('downloadProgress', info)
  })

  //安装前的事件
  autoUpdater.on("before-quit-for-update",()=>{
    win.webContents.send('message', "正在安装请稍后")
  })

  //询问服务器是否有更新。 使用该 API 前必须先调用 setFeedURL
  //查看是否有更新事件
  ipcMain.on('checkForUpdate', (event, data) => {
    console.log("检查更新")
    //将制动下载设置为false
    autoUpdater.autoDownload = false
    autoUpdater.checkForUpdates()
    win.webContents.send('message', "正在检查更新")
  })
  
  //  手动下载事件
  ipcMain.on('download', (event, data) => {
    //将制动下载设置为true
    autoUpdater.autoDownload = true
    autoUpdater.checkForUpdates()
  })

  //确定安装事件
  ipcMain.on('installApp', (event, data) => {
    autoUpdater.quitAndInstall()
  })
}

主进程文件background.js

import updateHandle from "./upload.js"

async function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    width: 1800,
    height: 1000,
    show: false,//设置默认不显示
    paintWhenInitiallyHidden: false,
    webPreferences: {

      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      enableRemoteModule: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
      nodeIntegrationInWorker: true,
    }
  })
  // 设置默认最大化
  win.maximize()
  //设置显示
  win.show()




  //  创建菜单对象
  // const menu = Menu.buildFromTemplate(getMenuConfig(win));
  //  设置应用菜单
  // Menu.setApplicationMenu(menu);
  //设置为null表示隐藏菜单栏
  Menu.setApplicationMenu(null);
  win.webContents.openDevTools()

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (process.env.BABEL_ENV === "development") win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }

  // 初始化Update配置,在创建口创建完成后
 updateHandle(win)
}

页面展示文件,接收和展示自动更新文件的内容upload.vue

<template>
    <div>
        <main>
            <div class="right-side">
                <div class="doc">
                    <div class="title alt">检查更新,不自动下载</div>
                    <el-button type="primary" round @click="checkUpdate">检查更新不自动下载, 不可用于开发环境</el-button>
                    <el-button type="primary" round @click="download">手动下载更新文件</el-button>
                    <el-button type="primary" round @click="installApp">安装</el-button>

                    <el-progress :text-inside="true" :stroke-width="20" :percentage="percent"></el-progress>
                    {{ appInfo }}
                    <div>{{ messageStr }}</div>
                    <br>
                    <div>{{ progressObj }}</div>
                    <div>{{ percent }}</div>
                    <br>
                    <div>{{ errorObj }}</div>
                    <div>{{ installObj }}</div>
                </div>
            </div>
        </main>
    </div>
</template>
 
<script>
let ipcRenderer = require("electron").ipcRenderer;

export default {
    name: "systemExample",
    data() {
        return {
            percent: 0,
            show: 'true',
            appInfo:{},
            messageStr:"",
            progressObj:{},
            errorObj:{},
            installObj:{},
        }
    },
    mounted() {

        // 主进程返回检查状态
        ipcRenderer.on("message", (e, message) => {
            this.$message.error(message)
            this.messageStr = message
            console.log("status--->", message)
        });
     
        // 更新进度
        ipcRenderer.on('downloadProgress', (e, progressObj) => {
            console.log("progressObj--->", progressObj);
            this.$message.error("正在下载")
            this.percent = (progressObj.percent).toFixed(2) || 0;

            console.log("this.percent---->", Math.trunc(this.percent))
            this.progressObj = progressObj
        });
        //接收更新的版本信息
        ipcRenderer.on("updateAvailable", (e, info) => {
            console.log("status--->", info)
            this.appInfo = info
        });
        //下载成功请求安装
        ipcRenderer.on("requestInstall", (e, info) => {
            this.installObj = info
            this.$confirm("下载完成,是否立即更新","提示",{
                    closeOnClickModal: false, // 禁止点击遮罩关闭弹框
                    closeOnPressEscape: false, // 禁止按 ECS 键关闭弹框
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning',
                }).then(()=>{
                    console.info("下载完成,选择立即更新")
                    ipcRenderer.send('installApp');
                    this.$message({
                      type: 'success',
                      message: '更新成功'  
                    })
                }).catch(()=>{
                    console.info("下载完成,选择取消更新")
                    this.$message({
                        type:'info',
                        message: '取消更新'
                    })
                })
        });
        //下载错误
        ipcRenderer.on("installErr", (e, error) => {
            this.errorObj = error
        });
        
    },

    methods: {
        // 下面方法点击按钮开始检查更新,
        checkUpdate() {
            ipcRenderer.send("checkForUpdate")
        },
        //安装
        installApp(){
            ipcRenderer.send("installApp")
        },
        //下载
        download(){
            ipcRenderer.send("download")
        }
    },
}

</script>
 
<style scoped>
</style>
 

实现了一个简单的自动更新

在使用electron.build打包的时候需要配置pluginOptions.electronBuilder.builderOptions.publish不然不会生成latest.yml文件,我的配置如下

"publish": [
          {
            "provider": "generic",
            "url": "http://192.168.0.148:80"
          }
        ]

测试可以使用http-erver运行一个简单的服务器,将我们打包好的高版本文件添加到服务器,将我们打包好的xxx.exe文件以及xxx Setup 1.2.0.exe.blockmap和latest.yml一起放入服务器,就可以实现自动更新了