Electron 在线热升级方案

一个项目正式交付客户使用了,对于版本的升级和代码更新是个一直困扰的问题,由于之前一直都是内部人员使用,可以让他们每次都下载完整压缩包(压缩后大概 40-50MB),但对于正式的终端用户,这个体验是不可接受。
参考过 Electron 官方文档,里面提及的方案兼容性并不好(Linux 不支持),需要准备的东西不少,而且交互上也不满意;以及参考了这篇 《我是如何实现electron的在线升级热更新功能的?》,最终按照里面的思路为该项目做了一个在线热升级的方案,本文介绍方案的细节及中间遇到的一些坑。

流程图

关键步骤

  1. 通过 app.getVersion() 获取到 App 的本地版本号,这个版本号来源于 package.json
  2. 获取线上的 App 版本信息,此处我们设计了两个版本信息(如图例子);
    2.1. “1.3.0” 为核心版本号,该版本号用于判断是否需要重新下载完整包(图中所示的对应不同平台有不同的下载地址,为 zip 扩展名的压缩包,至于为什么用 zip,下文会提及),比如当 Electron 核心版本升级,或者增加了 node_modules 依赖等情况;
    2.2. “1.3.3” 为热更新版本号,这种情况只需要更新特定的文件列表(图中所示的 upgrade_1.3.3.tar.gz),通常包含 package.jsonsrc/* 等;
    2.3. description 分别表示核心版本和热更新版本的描述;
  3. 对比本地版本号 (appVersion) 和线上的版本号 (onlineVersion),下面是版本号比较的代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /* v1 为本地版本号,v2 为线上版本号 */
    const compareVersion = (v1, v2) => {
    const vs1 = v1.toString().split('.'), vs2 = v2.toString().split('.');
    if (vs1.length !== vs2.length) {
    // 版本格式不一致
    return true;
    }
    for (let i = 0; i < vs1.length; ++i) {
    let diff = parseInt(vs2[i], 10) - parseInt(vs1[i], 10);
    if (diff < 0) {
    // vs1 其中一个版本号段小于 vs2
    return false;
    }
    if (diff > 0) {
    // vs2 其中一个版本号段大于 vs1
    return true;
    }
    }
    // 版本一致
    return false;
    };

    3.1. 对比本地版本号和线上核心版本号 (onlineVersion.version),如果小于线上版本号,则激活浏览器并进入下载,同时退出 App;
    3.2. 否则进行对比本地版本号和热更新版本号 (onlineVersion.upgrade.version),如果小于线上版本号,则下载热更新包;
  4. 根据 (upgrade.url) 下载热更新包、升级、重启 App,全部代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    const { app } = require('electron'),
    fs = require('fs-extra'), // 用于扩展内置 fs 方法
    request = require('request'), // 用于发起下载请求
    tar = require('tar'); // 用于执行 tar 解压缩
    //...
    // 下载热更新包代码
    new Promise((resolve, reject) => {
    request({
    url: onlineVersion.upgrade.url,
    encoding: null // encoding 为 null 使 body 生成为一个 Buffer
    }, (error, res, body) => {
    try {
    if (error || res.statusCode !== 200) {
    throw '请求失败';
    }
    // 保存到临时目录,temp 为 Electron 用户可写目录
    let tempPath = app.getPath('temp');
    let dir = fs.mkdtempSync(`${tempPath}/upgrade_`);
    // 创建 Buffer 流并解压
    let stream = new require('stream').Readable();
    stream.push(body);
    stream.push(null);
    stream.pipe(tar.extract({
    sync: true,
    cwd: dir
    })).on('close', () => {
    // 解压完毕,复制更新文件
    fs.copySync(dir, __dirname); // 解压至指定的目录,这里用 __dirname 为例
    fs.removeSync(dir); // 删除临时目录
    // 返回 true 表示需要重启
    resolve(true);
    });
    } catch (e) {
    reject('更新文件下载失败,请联系管理员');
    }
    }
    }).then(result => {
    if (result) {
    app.relaunch({ args: process.argv.slice(1) }); // 重启
    app.exit(0);
    }
    }).catch(e => {
    // e 错误
    });

  1. Windows 下可以直接把更新文件保存到 App 目录(没有权限问题),而在 Mac OSX 下,App 目录是无权写入的,而临时文件路径必须通过 app.getPath() 来获取,常用 temp 或者 userData 目录来保存解压文件;
  2. 压缩包我使用了 zip 一个原因是 Windows 下默认就能支持 zip 包解压,另一个主要原因是文件名编码问题,最初用 tar 来压缩文件,对于英文文件名是正常的,对于中文文件名,在 Mac OSX 下打包后在 Windows 是无法被正确解开的。而貌似 7z 生成的 zip 包会保存文件名字符集并能被 Windows 正确解开,所以在 gulp 中使用了 7z 来产生 zip 压缩包:
    1
    2
    3
    4
    const child = require('child_process');
    child.exec(`7z a -tzip -r ./app-{platform}_${packageInfo.version}.zip ./中文的应用程序名-${platform}`, (error, stdout, stderr) => {
    //...
    });

    而对于热更新包,由于源代码都是使用英文名,所以用 tar 来压缩没毛病:
    1
    2
    3
    4
    5
    6
    7
    8
    const tar = require('tar');
    tar.create({
    gzip: true,
    file: `./upgrade_${packageInfo.version}.tar.gz`,
    cwd: './build'
    }, [ 'package.json', 'src' ]).then(() => { // 保存 package.json 和 src 目录
    //...
    });


转载于:https://juejin.im/post/5c8b51fa5188257e8e232d13

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值