研究Electron自动更新 系列三【近8k字】「终于解决」

研究Electron自动更新 系列三【近8k字】「终于解决」这是继《研究 Electron 自动更新》系列的最后一篇,感谢大家的耐心阅读。 系列一从自动更新的方案深入地讲解了其中的原理,另外还讲解了两种打包方式。 系列二列举了开发中出现的三个问题,分别是“Can not find Squirrel”、“安装目录中packages文件夹和…

这是继《研究 Electron 自动更新》系列的最后一篇,感谢大家的耐心阅读。

系列一从自动更新的方案深入地讲解了其中的原理,另外还讲解了两种打包方式。

系列二列举了开发中出现的三个问题,分别是“Can not find Squirrel”、“安装目录中packages文件夹和Update.exe程序找不到”和“Error: spawn UNKNOWN”,从不同角度分析并且作了解答。

本文就继续系列二,再讲讲遇到的其他问题。

开发中存在的问题

本文首发于公众号「前端keep」,欢迎关注。

(四) Error Downloading Update: Command failed: 4294967295

1. 背景

自动更新过程中出现“Error Downloading Update: Command failed: 4294967295”的报错,这个 error 和系列二中的问题3Error: spawn UNKNOWN” 很类似,因为这个问题很常见,所以我要挑出来讲。

2. 原因分析

这个问题在 Squirrel.Windowsissueshttps://GitHub.com/Squirrel/Squirrel.Windows/issues/833)中也有, 其中的回答绕不过一点:程序的错误,远程发布文件是空的或损坏影响我们的更新。

3. 解决方式

对于开发者来说,我需要重新上传新的安装程序。还有可能是更新服务器提供的下载 nupkgurl 出错,这个需要通过 SquirrelSetup.log 去仔细检查,不难的。

对于用户来说,可能需要先卸载后重新安装新的版本。

(五) 更新后,老版本没有被替换

1. 背景

如果当前电脑上的应用版本是 0.0.1,服务器上最新是 0.0.2。自动更新完成后,多出来一个新版本的目录 app-0.0.2,但是没有覆盖 xxx 项目,桌面快捷方式打开的还是 xxx 项目里的旧版本。

究其原因,归咎于 nsis 没有集成 updateManage 机制。系列一和二已经描述过,就不再赘述。

研究Electron自动更新 系列三【近8k字】「终于解决」

图 7 安装目录

2. 解决方案

  1. 向服务器每隔一段时间发送当前版本的请求,询问其是否有新版本的应用(setFeedURLcheckForUpdates 方法实现);
  2. 当有更新进入 errorchecking-for-updateupdate-availableupdate-not-available 这些钩子方法时,写入日志;
  3. 更新进入 update-downloaded,提示用户更新完成,手动重启。然后,启动一个子进程去执行 bat 脚本,替换安装目录下面的旧版本。

xxx 项目的更新代码,见 update.js

import {autoUpdater} from 'electron'
// 服务器地址
const server = 'XXXXXXX'
const url = `${server}/update/${process.platform}/${app.getVersion()}/stable`
logger.info(`url:${url}`)
// 设置请求地址
autoUpdater.setFeedURL({
  url
})
logger.info(`process.ExecPath:${process.ExecPath}`)
// 检查更新
setInterval(() => {
  autoUpdater.checkForUpdates()
  logger.info('checkForUpdates')
}, 900000)

const appName = '应用更新'
const message = {
  error: '检查更新出错',
  checking: '正在检查更新……',
  updateAva: '下载更新包成功',
  updateNotAva: '现在使用的就是最新版本,不用更新',
  downloaded: '更新完成,请手动重启'
}
autoUpdater.on('error', error => {
  logger.error('There was a problem updating the application')
  logger.error(error)
})
.on('checking-for-update', function () {
    logger.info('当开始检查更新的时候触发')
  })
  .on('update-available', function () {
    logger.info('当有可用更新时发出,更新会自动下载')
  })
  .on('update-not-available', function () {
    logger.info('暂无更新')
  })
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
  logger.info('update-downloaded')
  logger.info(`releaseNotes:${releaseNotes}`)
  logger.info(`releaseName:${releaseName}`)

  dialog.showMessageBox({
    type: 'info',
    buttons: ['确定'],
    title: appName,
    message: process.platform === 'win32' ? releaseNotes : releaseName,
    detail: message.downloaded
  }).then((returnValue) => {
    if (returnValue.response === 0) {
      fs.writeFile('../releaseName.txt', releaseName, (err) => {
        if (err) {
          logger.error(err)
          throw err
        } else {
          var ls
          ls = childProcess.spawn('libs/Windows/adb/adb', ['kill-server'])
          ls.stdout.on('data', function (data) {
            logger.info('stdout: ' + data)
          })
          ls.stderr.on('data', function (data) {
            logger.error('stderr: ' + data)
          })
          ls.on('exit', function (code) {
            logger.info('目录替换程序开始运行')
            // 地址
            const a = process.cwd()
            logger.info('a ' + a)
            const arr = a.split('\\')
            logger.info('arr ' + arr)
            const pre = a.slice(0, -arr[arr.length - 1].length)
            logger.info('pre' + pre)
            process.chdir(pre)
            childProcess.Exec(`start /min "" "${pre}replace.bat" ${releaseName}`)
            setTimeout(() => {
              logger.info('xxx项目退出')
              app.quit()
            }, 1000)
          })
        }
      })
    }
  })
})

xxx 项目新版本替换旧版本的脚本,见 Replace.bat

chcp 65001
echo **更新即将完成,请勿关闭窗口!** >> replace.log
ping -n 5 127.0.0.1 >> replace.log
COPY ".\xxx项目\Uninstall xxx项目.Exe" app-%1 >> replace.log
COPY ".\xxx项目\uninstallerIcon.ico" app-%1 >> replace.log
RD /q /s ".\xxx项目" >> replace.log
ren app-%1 "xxx项目" >> replace.log
del "xxx项目.Exe" >> replace.log
exit

(六) Update.exe之外的操作无日志

1. 背景

主进程中加入 console,仅仅打印在终端上,并不能持久化日志。

更新过程中产生的日志都存储在 SquirrelSetup.log 中,但是仅仅只是 Update.exe 产出的日志。可是很多步骤需要输出更多的日志。 自动化工具中的部分更新日志采用 log4js 方案,将不同的日志类型输出在不同文件中。

2. 解决方案

xxx 项目的日志配置,见 log4js.js

const log4js = require('log4js')
const programName = 'xxx项目'
log4js.configure({
  appenders: {
    console: { // 记录器1:输出到控制台
      type: 'console'
    },
    log_file: { // 记录器2:输出到文件
      type: 'file',
      filename: `./logs/${programName}.log`, 
      maxLogSize: 20971520, 
      backups: 3, 
      encoding: 'utf-8' 
    },
    data_file: { // :记录器3:输出到日期文件
      type: 'dateFile',
      filename: `./logs/${programName}`,  
      alwaysIncludePattern: true,  
      daysToKeep: 7, 
      pattern: 'yyyy-MM-dd-hh.log', 
      encoding: 'utf-8' 
    },
    error_file: { // :记录器4:输出到error log
      type: 'dateFile',
      filename: `./logs/${programName}_error`, 
      alwaysIncludePattern: true, 
      daysToKeep: 7, 
    
      pattern: 'yyyy-MM-dd-hh.log', 
      encoding: 'utf-8' 
    }
  },
  categories: {
    default: {
      appenders: ['data_file', 'console', 'log_file'],
      level: 'info'
    }, // 默认log类型,输出到控制台 log文件 log日期文件 且登记大于info即可
    production: {
      appenders: ['data_file'],
      level: 'warn'
    }, // 生产环境 log类型 只输出到按日期命名的文件,且只输出警告以上的log
    console: {
      appenders: ['console'],
      level: 'debug'
    }, // 开发环境 输出到控制台
    debug: {
      appenders: ['console', 'log_file'],
      level: 'debug'
    }, // 调试环境 输出到log文件和控制台
    error_log: {
      appenders: ['error_file'],
      level: 'error'
    } // error 等级log 单独输出到error文件中 任何环境的errorlog 将都以日期文件单独记录
  }
})

module.exports = log4js

总结

解决的方式可能会很多,但是需要采用一种适合自己的方式钻研到底,坚持不懈,就一定能得到收获。

文中介绍了当前存在的问题、自动更新的方案、打包的两种方式和开发中存在的问题。其中,xxx 项目采用的是 squirrel.windows 的更新机制和 nsis 的自定义安装策略。

通过 electron-builder 将两者配置后,产出不同的安装程序 setup.exe 和更新程序 nupkg。然后将 nsissetup.exesquirrel.windows 中的 nupkg 上传到 electron-release-server 中。利用 electron-release-server 定时检查策略,对比本地版本和线上版本,自动下载依赖和程序,进行更新并且替换,做到用户无感知,操作不繁琐。

开发中遇到问题其实不止这些,由于篇幅问题,所以我总结了一部分常见的问题。

本文首发于公众号「前端keep」,欢迎关注。

最后,希望大家一定要点赞三连。

可以阅读我的其他文章,见blog地址

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13327.html

(0)

相关推荐

  • 数据采集介绍_数据收集的五种方法

    数据采集介绍_数据收集的五种方法1. 概述 现在学校越来越重视孩子课外知识的掌握,给孩子挑选课外书一般都是参考学校或者家长之间的推荐。 有时,也会想看看在儿童阶段,目前到底流行的是些什么样的书。 ​ 于是,就简单写了这个小爬虫,采集

    2023-04-30
    149
  • Python数据挖掘入门指南

    Python数据挖掘入门指南近年来,数据挖掘成为了各行业的热门方向,Python语言在数据挖掘领域的应用已经越来越广泛,成为专业人士和研究者们的首选语言之一。

    2024-04-16
    78
  • 陈宏申:浅谈京东电商商品文案挖掘难点与优化实践[亲测有效]

    陈宏申:浅谈京东电商商品文案挖掘难点与优化实践[亲测有效]导读: 在电商推荐中,除了推送商品的图片和价格信息外,文案也是商品非常重要的维度。基于编码器解码器范式的序列文本生成模型是文案挖掘的核心,但该种方法面临着两大技术挑战:一是文案生成结果不可靠和生成质量

    2023-05-19
    147
  • MySQL count知多少

    MySQL count知多少统计一个表的数据量是经常遇到的需求,但是不同的表设计及不同的写法,统计性能差别会有较大的差异,下面就简单通过实验进行测试(大家测试的时候注意缓存的情况,否则影响测试结果)。 1、 准备工作 为了后续测

    2023-02-13
    144
  • 泡泡后台Couchbase缓存使用经验分享「终于解决」

    泡泡后台Couchbase缓存使用经验分享「终于解决」一、导读 爱奇艺的社交业务“泡泡”,拥有日活用户6千万+,后台系统每日高峰期间接口QPS可以达到80K+,与视频业务的主要区别是泡泡业务更多地引入了与用户互动相关的数据,读、写的量均很大。无论是庞大的

    2023-01-31
    161
  • Ef core_efcore复杂查询

    Ef core_efcore复杂查询带着问题去思考,大家好! 前几天了解到EF Core的开发模式:DB First(数据库优先),Model First(模式优先),Code First(代码优先)。 我所接触的大多是DB First

    2023-02-08
    157
  • Python os.path.join函数实现路径拼接

    Python os.path.join函数实现路径拼接在Python中,os.path.join()函数是用于拼接路径的。该函数接受多个路径组件作为参数,返回这些组件的连接路径。

    2023-12-08
    117
  • Python os.environ模块:环境变量的管理

    Python os.environ模块:环境变量的管理os.environ模块是Python提供的用于对系统环境变量进行管理的工具,简单而言,它是一个存储环境变量的字典。环境变量是指在操作系统中定义的以键值对的形式存在的一系列变量,这些变量用于存储系统相关的信息,例如当前用户的登录名、操作系统的安装目录、Python安装路径等。在本文中,我们将详细介绍os.environ模块的用法,并演示如何使用os.environ来设置、获取和删除环境变量。

    2023-12-16
    124

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注