您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
     
   
 订阅
  捐助
Electron 自动更新的完整教程(Windows 和 OSX)
 
作者专栏昵称:思考中
70 次浏览     评价:  
  2020-3-27
 
编辑推荐:
本文主要讲解如何在Mac OSX 10.11 上执行操作进行Electron 应用实现自动更新,希望对您能有所帮助。
本文来自于SegmentFault,由火龙果软件Alice编辑推荐。

通过 Electron,你可能只需一眨眼的时间就完成了一个不错的桌面应用,并分发到用户手中。当你觉得自己能像一个侥幸的坏蛋一样轻松时,你可能会意识到你遗漏了一个重要的点:用户如何获取下一个版本呢?甚至该新版本新增了一些优秀的功能。当然,他们能删除后再重新安装该应用,但这难道不蹩脚吗?

快速浏览 Electron 文档 时,你会注意到该文档中含有 auto-updater 模块,它仅仅是另一个框架——Squirrel 的接口。Squirrel 会在背后检测(或你主动触发)是否有新版本、下载新版本,并在你启动或重启应用时自动更新应用。

但悲伤的是:实际实现起来并不是文档上写的这么简单。因为自动更新在 OSX 和 Windows 上的工作方式并不相同(目前并不支持 Linux),并且这两者的文档是分散在多个库(repository)中。我已经花费了大量的时间把该功能实现了。所以我觉得将我所学习到的知识总结成一篇教程是值得的,希望它能节省你的时间。

虽然这里所讲的一切应该均能在 Windows 和 OSX 上运行,但为了减少异议,我先声明我是在 Mac OSX 10.11 上执行的操作,除了为 Windows 系统构建安装包(在虚拟机上)。

应用打包

在实现自动更新之前,有一个重要的步骤 —— 打包。我假设大多数人已经知道如何通过 electron-packager 实现该操作,但有两件事是时常被忽略的。

{
"name": "MyApp",
"main": "app.js",
"private": true,
"productName": "MyApp",
"version": "1.0.0",
"author": "My Company Ltd",
"description": "MyApp",
"devDependencies": {
"electron-installer-squirrel-windows": "^1.3.0",
"electron-packager": "^5.1.1",
"electron-prebuilt": "0.36.7"
},
"scripts": {
"start": "NODE_ENV=development ./node_modules/.bin/electron .",
"pack:osx": "./node_modules/.bin/electron-packager . $npm_package_productName --app-version=$npm_package_version --version=0.36.7 --out=builds --ignore='^/builds$' --platform=darwin --arch=x64 --sign='Developer ID Application: My Company Ltd (ABCDEFGH10)' --icon=icon.icns --overwrite",
"pack:win": "./node_modules/.bin/electron-packager . $npm_package_productName --app-version=$npm_package_version --version=0.36.7 --out=builds --ignore='^/builds$' --platform=win32 --arch=ia32 --version-string.CompanyName='My Company Ltd' --version-string.LegalCopyright='Copyright (C) 2016 My Company Ltd' --version-string.FileDescription=$npm_package_productName --version-string.OriginalFilename='MyApp.exe' --version-string.InternalName=$npm_package_productName --version-string.ProductName=$npm_package_productName --version-string.ProductVersion=$npm_package_version --asar=true --icon=logo.ico --overwrite"
}
}

package.json

注意 package.json 的额外字段 —— productName、author 和 description,虽然这几个字段并不是打包必备的,但它们会在 Windows 的 Squirrel 安装包中使用到。

为应用执行代码签名(Code-signing)的这部操作并不是自动更新的必备步骤(译者注:也许作者当时的 Electron 版本的自动更新模块不必进行代码签名,但当前版本是必须要进行这部操作的,官方文档中写道:Your application must be signed for automatic updates on macOS. This is a requirement of Squirrel.Mac. ),但这是非常可取的操作。对于 OSX,你需要一个 Apple 的开发者认证,然后在 script 字段的 pack:osx 替换以下参数即可:

--sign='Developer ID Application: My Company Ltd (ABCDEFGH10)'

在 OSX 中,你可以通过 Keychain Access > My Certificates 查看(应用程序 -> 钥匙串 > 我的证书,如果有的话)。

我并没有在 Windows 上执行代码签名这项操作,但你可以看看该主题相关的优秀教程。

对于 Windows,推荐为 electron-packager 传递 version-string 的所有可选参数,如 company name、product name 等。因为一旦我们生成 Windows 的 Squirrel 安装包,该应用就能在 Windows 的『开始』菜单显示正确的元信息(metadata),而不是 Atom 的默认信息。

Atom Shell is now called Electron。

所以,让我们开始吧!

OSX

在 OSX 中,自动更新是通过 Squirrel.Mac 处理的,它是内置于 Electron 中。这意味着你只需打包你的应用,然后照常运行就好!

恩,其实不完全是。

Squirrel.Mac 的工作方式是通过访问一个你所提供的 API 『路径』(endpoint),判断是否有新版本。如果没有新版本,那么该路径应该返回 HTTP 204。如果有新版本,则它会期待接收一个 HTTP 200、且是 JSON 格式 的响应,其中包含一个 能获取 .zip 文件的 url。

PS:『路径』又称"终点"(endpoint),表示API的具体网址。

{
"url": "http://mysite.com/path/to/zip/MyApp.zip"
}

在得到该 url 后,Squirrel 会构造一个 application/zip 的请求去访问该 url,下载相应文件,然后触发最终事件(下载完成)让你知道更新包即将安装。对于你来说,所有事情的处理都是自动化的。

如果你不十分确定服务器程序应该长什么样,可看看下面的一个超级小型的 Node.js/Express 服务,假定它的目录结构如下:

└── releases
├── darwin
│ ├── 1.0.0
│ ├── 1.0.2
│ └── 1.0.3
└── win32

{
"name": "squirrel-version-checker",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "PORT=80 node app.js",
"dev": "./node_modules/.bin/nodemon app.js"
},
"dependencies": {
"express": "^4.9.8",
"morgan": "^1.3.2"
},
"devDependencies": {
"nodemon": "^1.8.1"
}
}

基于 Node 的更新服务 package.json

'use strict';
const fs = require('fs');
const express = require('express');
const path = require('path');
const app = express();
app.use(require('morgan')('dev'));
app.use('/updates/releases', express.static(path.join(__dirname, 'releases')));
app.get('/updates/latest', (req, res) => {
const latest = getLatestRelease();
const clientVersion = req.query.v;
if (clientVersion === latest) {
res.status(204).end();
} else {
res.json({
url: `${getBaseUrl()}/releases/darwin/${latest}/MyApp.zip`
});
}
});
let getLatestRelease = () => {
const dir = `${__dirname}/releases/darwin`;
const versionsDesc = fs.readdirSync(dir).filter((file) => {
const filePath = path.join(dir, file);
return fs.statSync(filePath).isDirectory();
}).reverse();
return versionsDesc[0];
}
let getBaseUrl = () => {
if (process.env.NODE_ENV === 'development') {
return 'http://localhost:3000';
} else {
return 'http://download.mydomain.com'
}
}
app.listen(process.env.PORT, () => {
console.log(`Express server listening on port ${process.env.PORT}`);
});

一个简单地、用于测试 Squirrel.Mac 自动更新的 Express 服务器

这将会从本地的文件系统进行分发文件,但这不是理想的处理方式。我的建议是:将这些文件放置在 Amazon S3。

Amazon S3:Amazon Simple Storage Service

然后你可以在开发环境下,通过 Electron 访问该路径:

http://localhost:3000/updates/latest?v=1.0.1

?v=1.0.1 是你当前应用的版本。

现在你已经拥有了服务器程序和路径了,那么在应用中处理更新操作就十分简单了。

在 Electron 的主进程文件中,引入 auto-updater 模块,然后获取当前系统和应用的版本:

const autoUpdater = require('auto-updater');
const appVersion = require('./package.json').version;
const os = require('os').platform();

然后配置路径,该路径会因系统(Windows 和 Mac)不同而有所差异(至于原因,会在 Windows 章节看到):

var updateFeed = 'http://localhost:3000/updates/latest';
if (process.env.NODE_ENV !== 'development') {
updateFeed = os === 'darwin' ?
'https://mysite.com/updates/latest' :
'http://download.mysite.com/releases/win32';
}
autoUpdater.setFeedURL(updateFeed + '?v=' + appVersion);

告诉 Electron 到哪里检测新版本

autoUpdater 模块提供了一些事件,你可通过渲染进程触发它们(译者注:通过 IPC 通讯模块),想获取更多信息,可查阅 auto-Updater 文档页面 。相关交互的实现决定取决于你如何处理这些事件(如发生错误等),并通知用户。但你最后一步应该做的是:

autoUpdater.quitAndInstall();

将上述语句放在主进程文件后,应用会以新本版的形式重启。赞!

Windows

如你想象的那样,在 Windows 上实现自动更新是通过 Squirrel.Windows。但它的处理方式与 OSX 完全不同。

与 Squirrel.Mac 不同的点在于:Squirrel.Windows 并不需要一个用于检测新版本的 API 路径,它需要的是一个文件服务器,所以你可以简单地将文件拖拽到 Amazon S3 bucket 上。另外,该 Squirrel 更新器并不内置于 Electron,它是一个第三方依赖。这意味着你需要为你所打包的 Windows 应用生成一个安装器,这样它才会包含 Squirrel 更新器。

Amazon S3 bucket:S3 的数据存储结构非常简单,就是一个扁平化的两层结构:一层是存储桶(Bucket,又称存储段),另一层是存储对象(Object,又称数据元)。

好消息是:Windows 的安装包和更新器的运行过程顺滑的。因为当你启动 Setup.exe 时,你会发现安装和启动该应用是迅速的。没有无聊的安装向导和一直按“下一步”、最后按“完成”的步骤,不然与大多数 Windows 安装器如出一辙。当然,它也能生成 delta packages,这让你在执行更新时,不必下载整个应用,这真的是一流啊。

译者注:我通过 electron-builder 生成的 Windows 安装包与我们常见的软件安装界面不太一样,他没有安装向导和点击“下一步”,只有一个安装时的 gif 动画(默认的 gif 动画如下图),因此也就没有让用户选择安装路径等权利。也许作者习惯了 Mac 的安装方式(即下面第二幅图),所以会觉得 Windows 的安装包比较繁琐。

Mac 常见的安装模式,将“左侧的应用图标”拖拽到“右侧的 Applications”即可

如果你想为 Windows 应用生成常见的、需要点击“下一步”的(即用户可自定义的)安装包,可以通过 NSIS 程序,具体可看这篇教程《[教學]只要10分鐘學會使用 NSIS 包裝您的桌面軟體–安裝程式打包。完全免費。》。当然,前提还是通过 electron-packager 打包程序。

NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的。NSIS 的脚本语言和通常的编程语言有类似的结构和语法,但它是为安装程序这类应用所设计的。

坏消息是(至少对于 Mac 用户):我不能在 OSX 上正确地生成安装包,所以我建议你下载一个 Windows 虚拟机(如 VirtualBox、parallels),并安装 Node.js。

译者注:我通过 electron-builder,可在 MacOS 中直接(即不通过虚拟机)生成 Windows 安装包(即Setup.exe)。具体可 查看这里。

假设你已经配置好并设置了正确的更新源,那么在上述 OSX 章节的代码基础上,还需要处理一些 Squirrel.Windows 事件,这些事件与 OSX 上的不同。你可以查看该 案例。然而,这里提供一个更简单的方式,仅需安装 electron-squirrel-startup npm 模块:

npm install electron-squirrel-startup --save-dev

然后在 Electron 的主进程文件顶部添加以下一行语句:

if (require('electron-squirrel-startup')) return;

Squirrel.Windows 事件应该被尽早处理,显然,这是要走的路。

最后,为了生成安装包,我们会使用 Atom 的 grunt-electron-installer。为什么它是一个 grunt 插件,而不是一个简单的命令行工具——我不知道,但它就是解决方法。

更新:Electron 团队开发了一个独立的安装器打包工具——electron-winstaller,它拥有与 grunt task 同样的 API

将 Electron-packager 生成的 win32 文件夹打包压缩(zip),然后将其复制到虚拟机上。在该文件夹外(译者注:在解压后),你需要配置 grunt task,该 task 会生成安装包,因此你应该首先安装所有依赖:

npm install -g grunt-cli
npm install grunt grunt-electron-installer --save-dev

假设 Windows 编译后的包放置在一个称为 MyApp-win32-ia32 的文件夹下。下面展示 Gruntfile 的样子:

module.exports = function(grunt) {
grunt.initConfig({
'create-windows-installer': {
ia32: {
appDirectory: './MyApp-win32-ia32',
outputDirectory: './dist',
name: 'MyApp',
description: 'MyApp',
authors: 'My Company Ltd',
exe: 'MyApp.exe'
}
}
});
grunt.loadNpmTasks('grunt-electron-installer');
};

需要注意的是:如果你想为你的文件和安装包进行代码签名(code-sign)操作,你也需要为该 task 配置提供所有参数。

运行该 grunt task 后,会在 ./dist 目录下产生一堆文件:

grunt create-windows-installer

你预期看到的与下面类似:

└── dist
├── MyApp.1.0.0.nupkg
├── MyApp-1.0.0-full.nupkg
├── RELEASES
├── Setup.exe

在下一次发布时,该安装器也会自动生成一个 delta packages。

现在进行最简单的一步 —— 拖拽这些文件到 S3 bucket 进行上传。然后 url 指向该文件夹(包含 RELEASES 和 nupkg 文件)。当应用运行在 Windows 系统上时,它会将该 url 设置到 updateFeed 参数上(因为我们在先前的 OSX 章节处已实现)。

注意:目前有一个与安装器的 node-rcedit 模块相关的问题,该模块会在你尝试去修改 .exe 文件的一些元信息和替换默认图标(icon)时抛出错误。你可以在 这里查看该 issue。因此,目前如果你想为安装器文件修改 icon 或为其赋予实际数据,你可能不得不手动地通过 ResHacker 进行修改。

结束语

希望这篇文章能作为一个好的起点,能帮助和服务每一个正在为 Electron 应用实现自动更新的朋友们。

 
   
70 次浏览  评价: 差  订阅 捐助
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程