【electron】应用在线升级

2023-11-07

流程

 

1.设置feedUrl

  • 每次登录应用后更新feedUrl,如果需要灰度测试。可以添加uuid的参数,由服务端判断是否命中

注意:feedUrl需要在checkUpdate之前调用

// 主进程

// 初始化
  private cancellationToken?: CancellationToken

  private isForceUpdate = false

  private updating = false

  autoUpdater.logger = log
    autoUpdater.autoDownload = false
    autoUpdater.autoInstallOnAppQuit = true
    autoUpdater.retryOptions = {
      max: 15,
      interval: 3000,
    }

    const { productId } = environment


    // 登录后更新FeedUrl
    ipcMain.on(UpdateChannel.setFeedUrl, (event: any) => {
      autoUpdater.setFeedURL(
        `${environment.autoUpdaterUrl}?_productId=${productId}&version=${
          environment.appVersion
        }`,
      )
    })


//渲染进程
  /**
   * hooks中设置检查更新的连接
   */
  const setFeedUrl = useCallback((userId?: number) => {
    ipcRenderer.send(UpdateChannel.setFeedUrl, userId)
  }, [])


  // app中调用setFeedUrl

2.检查更新

  • 渲染进程调用checkUpDate方法检查更新,主进程中监听更新消息
  • 可以传入userAction参数,判断是否由用户主动检查更新
   
// 渲染进程

// app中调用checkUpdate(),注意在seetFeedUrl之后

  /**
   * 检查更新
   */
  const checkUpdate = useCallback((userAction = false) => {
    ipcRenderer.send(UpdateChannel.CheckUpdate, userAction)
  }, [])


// 主进程
 ipcMain.on(UpdateChannel.CheckUpdate, (event, userAction = false) => {
// 本地开发不执行更新操作
const serve = process.defaultApp ?? false
      if (serve) {
        return
      }
      this.update(userAction).then((updateInfo) => {
        if (updateInfo) {
          event.sender.send(
            UpdateChannel.CheckResult,
            updateInfo,
            this.isForceUpdate,
          )
        } else if (userAction) {
          event.sender.send(UpdateChannel.CheckResult, null, this.isForceUpdate)
        }
      })
    })

3.监听检查结果

// 渲染进程


  /**
   * 下载进程监听检查更新的结果,如果有更新信息则弹出更新信息弹窗
   * 如果是用户主动出发检查更新,则提示当前已经是最新版本
   */
  ipcRenderer.on(
    UpdateChannel.CheckResult,
    (event: any, updateInfo: UpdateInfo, force: boolean) => {
       // 服务端判断需要更新才给出更新提示
    // 看接口返回逻辑是需要更新就返回新的信息,如果只是返回线上版本,则需要自己跟本地版本大小,判断是否需要更新
      if (updateInfo) {
        versionInfoDialog(updateInfo, force)
      } else {
        message.info('当前已是最新版本,无需更新', 1500)
      }
    },
  )

/**
   * 展示更新信息弹窗,如果是强制更新,则只能退出和更新
   * 点击立即更新则开始下载
   */
  const versionInfoDialog = useCallback(
    (updateInfo: UpdateInfo, force: boolean) => {
      setForceUpdate(force)
  
      const { version: ignoreVersion } = getItem(
        UpdateStorage.IGNORE_VERSION_KEY,
      ) || { version: '0.0.0' }
      const { time } = getItem(UpdateStorage.LAST_LATER_TIME) || { time: 0 }

      // NOTE: 强升不可跳过升级,每次打开都提醒,非强制升级和已经忽略过的版本不弹窗
      if (
        !force
        && updateInfo.version
        && ignoreVersion !== '0.0.0'
        && versionCompareLarger(updateInfo.version, ignoreVersion)
        && getZeroTimestamp(Date.now().valueOf()) - getZeroTimestamp(time) < BREAK_TIME
      ) {
        return
      }

      // 记录更新版本,下载弹窗中展示
      setUpdateVersion(String(updateInfo.version))
       // 记录已展示的更新提示的版本和提示时间,每天提醒一次
      setItem(
        UpdateStorage.IGNORE_VERSION_KEY,
        {
          version: updateInfo.version,
        },
      )
      setItem(UpdateStorage.LAST_LATER_TIME, {
        time: Date.now(),
      })
// 弹窗提示更新,展示版本信息和更新内容点击确认调用startDownload(),开始下载更新
  //点击取消如果是强制更新则退出应用,如果不是则关闭弹窗
  //        if (force) {
  //          exitApp()
  //        } else {
  //          closePopupConfirm()
  //       }
    },
    [
// 依赖
    ],
  )

4.下载更新

  • 渲染进程点击开始更新,主进程监听开始下载
  • 下载之前再次检查更新信息
  • 渲染进程监听下载进度变化,及时更新
// 渲染进程 
/**
   * 开始下载,保存下载时间
   */
  const startDownload = useCallback(
    () => {
      showProgressBar()
      ipcRenderer.send(UpdateChannel.StartDownload)
    },
    [showProgressBar],
  )

/**
   * 渲染进程监听下载进度,更新进度条
   */
  ipcRenderer.on(UpdateChannel.Progress, (event: any, value: number) => {
    setProgressValue(Math.floor(value))
  })


  /**
   * 渲染进程监听更新错误信息,给出error dialog
   */
  ipcRenderer.on(UpdateChannel.Error, () => {
    errorDialog()
  })


//主进程
    /**
     * 监听渲染进程的开始下载事件
     */
    ipcMain.on(UpdateChannel.StartDownload, (event) => {
      this.download(event)
    })


// 下载方法
  public download(event: IpcMainEvent) {
    this.updating = true
    const downloadProgress = (info: any) => {
// 更新下载进度
      event.sender.send(UpdateChannel.Progress, info.percent)
    }

// 下载出错
    const downloadError = (e: unknown) => {
      this.updating = false
      this.cancellationToken?.cancel()
      autoUpdater.off('download-progress', downloadProgress)
      autoUpdater.off('network-error', downloadError)
      event.sender.send(UpdateChannel.Error)
      log.error(e)
    }

// 下载完成
    const downloadFinish = () => {
      this.updating = false
      autoUpdater.off('download-progress', downloadProgress)
      autoUpdater.off('network-error', downloadError)

      event.sender.send(UpdateChannel.Finish)
    }

    autoUpdater.once('update-downloaded', downloadFinish)
    autoUpdater.on('download-progress', downloadProgress)
    autoUpdater.on('network-error', downloadError)
    autoUpdater.on('error', downloadError)
    process.once('uncaughtException', (e) => {
      if (this.isNetworkError(e)) {
        downloadError(e)
      } else {
        throw e
      }
    })

// 检查更新信息
    this.update()
      .then((updateInfo) => {
        if (updateInfo) {
          autoUpdater
            .downloadUpdate(this.cancellationToken)
            .catch(downloadError)
        }
      })
      .catch((e) => {
        log.error(e)
      })
  }


  public update(userAction = false) {
    this.isForceUpdate = false
    return autoUpdater.checkForUpdates().then((result) => {
      // 拿到更新信息
      const { updateInfo, cancellationToken } = result
      // 判断是否有可用更新,当更新版本大于当前版本时,则有可用更新
      const isUpdateAvailable = autoUpdater.currentVersion.compare(updateInfo.version) < 0
      // 判断是否需要强制更新,当最小要求版本大于当前版本时,则需要强制更新
      // const isForceUpdateAvailable =
      //   autoUpdater.currentVersion.compare(updateInfo.minimumVersion) < 0;
      // 当更新可用时,拿到取消更新的cancellationToken,可用于在下载过程中,中断下载
      this.cancellationToken = cancellationToken

      if (updateInfo.forceUpdate) {
        this.isForceUpdate = true
        return updateInfo
      }

      if (!updateInfo.forceUpdate && isUpdateAvailable) {
        return updateInfo
      }

      if (userAction) {
        return undefined
      }
      return undefined
    })
  }

 public isNetworkError(errorObject: Error) {
    return (
      errorObject.message === 'net::ERR_INTERNET_DISCONNECTED'
      || errorObject.message === 'net::ERR_PROXY_CONNECTION_FAILED'
      || errorObject.message === 'net::ERR_CONNECTION_RESET'
      || errorObject.message === 'net::ERR_CONNECTION_CLOSE'
      || errorObject.message === 'net::ERR_NAME_NOT_RESOLVED'
      || errorObject.message === 'net::ERR_CONNECTION_TIMED_OUT'
      || errorObject.message === 'net::ERR_NETWORK_CHANGED'
    )
  }

5.下载完毕

  • 下载完毕后执行quitAndInstall(),windows下载的是exe,如果打包方式为portable则点击安装后会直接安装完毕,如果打包方式为nsis,则需要重新走一遍安装流程
// 渲染进程  
/**
   * 关闭并安装
   */
  const quitAndInstall = useCallback(() => {
    ipcRenderer.send(UpdateChannel.Install)
  }, [])


// 主进程

    /**
     * 监听渲染进程的安装事件
     */
    ipcMain.on(UpdateChannel.Install, () => {
      autoUpdater.quitAndInstall()
    })

流程处理

  • 在下载中途理论上不能关闭下载流程,如果用户需要关闭,需要中断下载流程
// 渲染进程
  const cancelDownload = useCallback(() => {
    ipcRenderer.send(UpdateChannel.CancelDownload)
  }, [])

// 主进称
  public cancelDownloading() {
    this.updating = false
    this.cancellationToken?.cancel()
  }
  •  以防万一,在应用关闭的时候需要取消下载流程
  • // 都是主进称 
     win.on('close', () => {
        updater.cancelDownload()
      })
    
    
      public cancelDownload() {
        if (this.updating) {
          this.cancellationToken?.cancel()
          app.quit()
        }
      }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【electron】应用在线升级 的相关文章

随机推荐

  • vscode 配php环境的正确姿势

    看了很多网上的教程 很乱 很难找到一篇好的文章 经过多重筛选以及我的亲身实践 终于找到了一个很好地办法 因为本人耐心比较有限 写的很不详细 都是一些大纲 以及本人遇到的一些问题的总结 遇到啥问题 多百度 多自己动手试试吧 搭环境这个东西 烦
  • Listary 文件搜索浏览增强工具

    Listary介绍及升级 Listary简介 Listary下载 Listary安装 Listary使用 Listary 升为专业版 Listary Pro Listary简介 Listary是一款强大的全盘文件搜索 软件启动效率工具 双击
  • h5页面做微信公众号分享,一直出现无效签名,错误码:63002,invalid signature

    刚接手参与一个h5页面的项目 需要分享到微信 能看到数据正常的写入 但是偏偏分享出去就没有了设置好的标题 描述 和图片 打开的链接也是自己分享时所在的页面url 期间一直有报无效签名的错 不管是用原生微信还是插件去写 都一样 找了两天都没找
  • mysql中常见的聚合函数

    1 聚合函数的介绍 聚合函数又叫组函数 通常是对表中的数据进行统计和计算 一般结合分组 group by 来使用 用于统计和计算分组数据 常用的聚合函数 count col 表示求指定列的总行数 max col 表示求指定列的最大值 min
  • VS Code常用快捷键

    1 删除一行 快捷键 ctrl shift K 光标停在要删除的行 然后按下ctrl shift K即可删除该行 2 选定多个相同的单词 快捷键 ctrl d 先双击选定一个单词 然后按下 ctrl d 可以往下依次选择相同的单词 3 快速
  • 韩顺平java基础学习笔记一

    目录 面向对象编程 基础部分 7 1类和对象 7 2 成员方法 7 3 成员方法传参机制 非常非常重要 7 4 方法递归调用 非常非常重要 比较难 7 5 方法重载 OverLoad 7 6 可变参数 7 7 作用域 7 8 构造方法 构造
  • latex插图位置问题

    使用figure会进行浮动环境 这样插的图latex会自动调整 一般我们不需要这样的功能 可以使用 code begin figure h end figure code 或者使用 includegraphics 插入 如果列figure估
  • 【大数据】CDC 技术:变化数据捕获

    CDC 技术 变化数据捕获 1 什么是 CDC 2 批处理 vs CDC 3 四种 CDC 的实现方法 3 1 表元信息 Table metadata 3 2 表求差 Table differences 3 3 数据库触发器 Trigger
  • 华为OD机试-多个数组合并

    题目描述 现在有多组整数数组 需要将他们合并成一个新的数组 合并规则从每个数组里按顺序取出固定长度的内容 合并到新的数组 取完的内容会删除掉 如果改行不足固定长度 或者已经为空 则直接取出剩余部分的内容放到新的数组中继续下一行 输入描述 第
  • 关于《Python黑帽子:黑客与渗透测试编程之道》的学习笔记

    本篇文章是学习 Python黑帽子 黑客与渗透测试编程之道 的笔记 会持续地将书上的代码自己敲一遍 从而让自己对Python的安全编程有更多的了解 同时希望各位可以给给建议 不足之处太多了 第一章 设置Python环境 Kali Linux
  • 机器学习实战学习笔记(十三)利用SVD简化数据

    PS 该系列数据都可以在图灵社区 点击此链接 中随书下载中下载 如下 1 SVD的应用 奇异值分解 优点 简化数据 去除噪声 提高算法的结果 缺点 数据的转换可能难以理解 适用数据类型 数值型数据 1 1 隐形语义索引 最早的SVD应用之一
  • 并发测试异常[Address already in use: no futher infomation]

    前言 测试的同事反馈接口返回异常 如下 I O error on POST request for 接口地址 Address already in use connect nested exception is java net BindE
  • 动漫短视频在哪下载?3个网站教你快速找到

    动漫作为新兴的朝阳产业 受众群体越来越广 动漫短视频剪辑也越来越多 但很多人都有一个疑惑 动漫短视频在哪下载 素材怎么找呢 今天就给大家介绍一下 01 易撰 首先就是易撰 易撰视频库可以说是全网最全面的视频素材库了 包含了抖音 快手 美拍等
  • Mysql操作

    完成下面的操作 请写出修改表的SQL语句并运行 1 现要新增两个字段 籍贯和邮箱 A native 籍贯 数据类型为20位可变长字符串 放在性别列的后面 B email 邮箱 数据类型为50位可变长字符串 放在最后一列 2 设学生表stud
  • Golang笔记-大小写作用范围

    验证作用范围所使用的目录结构 其中main go内容如下 package main import encoding json fmt practice mulu1 practice mulu2 practice mulu4 1 即使mulu
  • Kubernetes入门学习-十九-网络配置插件flannel-01

    最近学习k8s遇到很多问题 建了一个qq群 153144292 交流devops k8s docker等 Kubernetes的网络模型和网络策略 Kubernetes网络模型和CNI插件 在Kubernetes中设计了一种网络模型 要求无
  • Redhat 设置北京时间

    Redhat 设置北京时间 系统版本 查看当前时间 修改时区 修改时间 系统版本 redhat 版本 6 5 wuxt localhost cat etc redhat release Red Hat Enterprise Linux Se
  • HiBlock社区:区块链链上数据的认知与探索

    跨界知识聚会系列文章 知识是用来分享和传承的 各种会议 论坛 沙龙都是分享知识的绝佳场所 我也有幸作为演讲嘉宾参加了一些国内的大型会议 向大家展示我所做的一些成果 从听众到演讲感觉是不一样的 把知识分享出来 你才能收获更多 关于作者 张丹
  • Pycharm常用快捷键及设置

    Pycharm 一 Pycharm常用快捷键 二 常用设置 1 代码提示忽略大小写敏感 2 滚轮缩放和自动换行 包括编辑栏和控制台 3 字体与代码配色 仅供参考 一 Pycharm常用快捷键 Ctrl 行注释 Ctrl D 复制选定的区域或
  • 【electron】应用在线升级

    流程 1 设置feedUrl 每次登录应用后更新feedUrl 如果需要灰度测试 可以添加uuid的参数 由服务端判断是否命中 注意 feedUrl需要在checkUpdate之前调用 主进程 初始化 private cancellatio