昨天我们谈论了 Electron 应用程序手动更新的流程 ,
今天 , 我们就在 app 开启时放入昨天那些手动更新的流程 ,
让我们的 app 有自动更新的功能 (^.^)/
类型 | 流程 |
---|---|
Portal | 下载新执行档 => 取代旧档 |
NSIS | 下载安装档 => 执行安装 |
Portal 的更新与 NSIS 的更新流程差别不大 , 因此下方本鲁只介绍 NSIS 的更新流程 ,
相信厉害的邦友们 , 必定能举一反三 , 写出 Portal 版本的更新自动化
我们利用 download.js 辅助我们下载新版的 exe 档案
首先 , 制作 downloadUtil.js 方便我们之後操作下载行为
// utils/downloadUtil.js
const download = require('download');
const fs = require('fs');
const _ = require('lodash');
const fileDownload = (url, dest) => {
// duplexStream is a Promise & EventEmitter
const duplexStream = download(url);
let downloadedLength = 0;
const writeStream = fs.createWriteStream(dest);
// 完成写入档案到指定位置
writeStream.on("finish", () => duplexStream.emit('write-finish'));
// 写入档案出错时
writeStream.on("error", err => duplexStream.emit('write-error', err));
// 限制每 0.5 秒至多执行 1 次
const throttleFunc = _.throttle(func => func(), 500);
duplexStream.on('response', res => {
const totalLength = res.headers['content-length'];
res.on('data', data => {
downloadedLength += data.length;
// 因为 duplexStream 是 EventEmitter 所以 emit channel : "got-data"
const params = {data, downloadedLength, totalLength};
throttleFunc(() => duplexStream.emit('got-data', params));
});
});
duplexStream.on("error", err => console.error(err));
duplexStream.pipe(writeStream);
// duplexStream.pause(); // 下载暂停
// duplexStream.resume(); // 下载继续
return duplexStream;
}
module.exports = fileDownload;
如何使用 downloadUtil.js ?
const doDownload = (url, dest, cb) => {
return new Promise((resolve, reject) => {
downloadUtil(url, dest)
.on('got-data', ({downloadedLength, totalLength}) => {
const saved = new Intl.NumberFormat().format(downloadedLength);
const total = new Intl.NumberFormat().format(totalLength);
const percent = ((downloadedLength / totalLength) * 100).toFixed(4)
console.log(`downloaded : ${saved} / ${total} ( ${percent} % ) `);
// 取得资料时 , 呼叫回呼函式 cb
cb && cb({downloadedLength, totalLength});
})
.on('write-finish', resolve)
.catch(reject);
})
}
显示对话框 , 让使用者决定是否要更新到最新的版本
// 更新对话框
function createUpdateWindow() {
const win = new BrowserWindow({
width: 400,
height: 200,
frame: false, // 标题列不显示
transparent: true, // 背景透明
autoHideMenuBar: true, // 工具列不显示
webPreferences: {
nodeIntegration: true,
},
});
win.loadFile('./update.html');
return win;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Update Confirm</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<style>
body {
user-select: none;
height: 100%;
background-color: #3cc245;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
margin: 20px;
}
</style>
</head>
<body>
<h1>有新的版本可使用 , 您是否要更新 ?</h1>
<div class="progress" style="width: 150px;display: none">
<div class="progress-bar progress-bar-striped progress-bar-animated"></div>
</div>
<div id="btn-group">
<button class="btn btn-primary" onclick="confirm()">更新</button>
<button class="btn btn-secondary" onclick="cancel()">取消</button>
</div>
<script>
// ...JS code , 可参考 https://github.com/andrew781026/ithome_ironman_2020/blob/master/day-36/update.html
</script>
</body>
</html>
完整下载安装档後 , 利用 spawn 执行它
利用昨天整理的 doInstall 函式 , 执行之
如有需求背景执行 , 可追加 args 参数
参数 | 说明 |
---|---|
--updated |
更新模式 |
/S |
背景执行 ( silent mode ) |
--force-run |
安装完成 , 执行应用程序 |
const doInstall = (exe = 'installer_path', args = ["--updated"]) => {
return new Promise((resolve, reject) => {
const process = spawn(exe, args, {
detached: true, // 让执行绪与 NodeJS 脱钩
stdio: "ignore",
})
process.on("error", error => reject(error))
process.unref()
if (process.pid) resolve(true);
})
}
利用 spawn 执行时 , 会出现安装画面
之後你利用捷径 开启应用程序 , 就会看到更新後的版本了 !
更新
不执行更新
下方为范例的安装档
今年小弟第一次参加 `铁人赛` , 如文章有误 , 请各位前辈提出指正 , 感谢 <(_ _)>
<<: [鼠年全马] W39 - 使用Vuex管理资料状态(下)
CTF 全名 Capture The Flag,并且分为下列几类的解题方式 解谜式(Jeopard...
地球绕着太阳转 教学原文参考:地球绕着太阳转 这篇文章会介绍,如何在 Scratch 3 里使用重复...
如果有用过 HE 提供的 Tunnel Broker 服务的话,应该对 SIT 隧道不陌生。 但是,...
在昨日的文章中,简单地向各位展示直接藉由Function抓取API 所能得到的架构会是何者 而今天...
大家好,我是YIYI,今天我要来检讨一下目前的问题~ 问题 第一个部分是页面,我认为可以再增加一些页...