perf: 优化客户端自动更新

This commit is contained in:
kuaifan 2022-01-15 23:36:21 +08:00
parent c6ebe994cc
commit 2a8e030fb4
4 changed files with 117 additions and 85 deletions

117
electron/main.js vendored
View File

@ -14,7 +14,7 @@ let mainWindow = null,
devloadUrl = "", devloadUrl = "",
devloadCachePath = path.resolve(__dirname, ".devload"), devloadCachePath = path.resolve(__dirname, ".devload"),
downloadList = [], downloadList = [],
downloadCacheFile = path.join(app.getPath('cache'), config.name + '.downloadCache'); downloadCacheFile = path.join(app.getPath('cache'), config.name, '.downloadCache');
if (fs.existsSync(devloadCachePath)) { if (fs.existsSync(devloadCachePath)) {
devloadUrl = fs.readFileSync(devloadCachePath, 'utf8') devloadUrl = fs.readFileSync(devloadCachePath, 'utf8')
@ -23,6 +23,39 @@ if (fs.existsSync(downloadCacheFile)) {
downloadList = utils.jsonParse(fs.readFileSync(downloadCacheFile, 'utf8'), []) downloadList = utils.jsonParse(fs.readFileSync(downloadCacheFile, 'utf8'), [])
} }
function downloadUpdate(item) {
const chain = item.getURLChain()
if (chain.length == 0) {
return
}
let currentState = item.getState()
if (currentState == "progressing" && item.isPaused()) {
currentState = "paused"
}
//
const downloadItem = downloadList.find(item => item.url == chain[0])
if (downloadItem && downloadItem.state != currentState) {
downloadItem.state = currentState;
downloadItem.result = {
url: item.getURL(),
name: item.getFilename(),
savePath: item.getSavePath(),
mimeType: item.getMimeType(),
totalBytes: item.getTotalBytes(),
chain,
};
fs.writeFileSync(downloadCacheFile, utils.jsonStringify(downloadList), 'utf8');
//
if (currentState == 'completed') {
mainWindow.webContents.send("downloadDone", downloadItem)
log.info("下载完成", downloadItem)
} else {
mainWindow.webContents.send("downloadUpdate", downloadItem)
log.info("下载更新", downloadItem)
}
}
}
function createMainWindow() { function createMainWindow() {
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 1280, width: 1280,
@ -69,37 +102,12 @@ function createMainWindow() {
}) })
mainWindow.webContents.session.on('will-download', (event, item) => { mainWindow.webContents.session.on('will-download', (event, item) => {
item.setSavePath(path.join(app.getPath('cache'), item.getFilename())); item.setSavePath(path.join(app.getPath('cache'), config.name, item.getFilename()));
item.on('done', (event, state) => { item.on('updated', () => {
try { downloadUpdate(item)
const info = { })
state, item.on('done', () => {
name: item.getFilename(), downloadUpdate(item)
url: item.getURL(),
chain: item.getURLChain(),
savePath: item.getSavePath(),
mimeType: item.getMimeType(),
totalBytes: item.getTotalBytes(),
};
mainWindow.webContents.send("downloadDone", info)
//
if (info.state == "completed") {
// 下载完成
info.chain.some(url => {
let download = downloadList.find(item => item.url == url)
if (download) {
download.status = "completed"
download.info = info
}
})
fs.writeFileSync(downloadCacheFile, utils.jsonStringify(downloadList), 'utf8');
} else {
// 下载失败
info.chain.some(url => {
downloadList = downloadList.filter(item => item.url != url)
})
}
} catch (e) { }
}) })
}) })
} }
@ -201,26 +209,33 @@ ipcMain.on('inheritClose', (event) => {
* @param args {url} * @param args {url}
*/ */
ipcMain.on('downloadFile', (event, args) => { ipcMain.on('downloadFile', (event, args) => {
const download = downloadList.find(({url}) => url == args.url);
if (download) {
if (download.status == "completed") {
if (fs.existsSync(download.info.savePath)) {
log.warn("已下载完成", args)
mainWindow.webContents.send("downloadDone", download.info)
} else {
log.info("开始重新下载", args)
download.status = "progressing"
mainWindow.webContents.downloadURL(args.url);
}
} else {
log.warn("已在下载列表中", args)
}
} else {
log.info("开始下载", args)
downloadList.push(Object.assign(args, { status: "progressing" }))
mainWindow.webContents.downloadURL(args.url);
}
event.returnValue = "ok" event.returnValue = "ok"
//
let appendJson = {state: "progressing", startTime: utils.Time()}
let downloadItem = downloadList.find(({url}) => url == args.url)
if (downloadItem) {
switch (downloadItem.state) {
case "completed":
if (fs.existsSync(downloadItem.result.savePath)) { // 下载完成,文件存在
log.info("下载已完成", downloadItem)
mainWindow.webContents.send("downloadDone", downloadItem)
return
}
break;
case "progressing":
if (downloadItem.startTime + 480 > utils.Time()) { // 下载中未超时超时时间8分钟
log.info("下载已存在", downloadItem)
return;
}
break;
}
downloadItem = Object.assign(downloadItem, appendJson)
} else {
downloadList.push(downloadItem = Object.assign(args, appendJson))
}
fs.writeFileSync(downloadCacheFile, utils.jsonStringify(downloadList), 'utf8');
mainWindow.webContents.downloadURL(downloadItem.url);
log.info("下载开始", downloadItem)
}) })
/** /**

17
electron/utils.js vendored
View File

@ -252,4 +252,21 @@ module.exports = {
let domain = (weburl + "").match(urlReg); let domain = (weburl + "").match(urlReg);
return ((domain != null && domain.length > 0) ? domain[2] : ""); return ((domain != null && domain.length > 0) ? domain[2] : "");
}, },
/**
* 返回10位数时间戳
* @param v
* @returns {number}
* @constructor
*/
Time(v = undefined) {
let time
if (typeof v === "string" && this.strExists(v, "-")) {
v = v.replace(/-/g, '/');
time = new Date(v).getTime();
} else {
time = new Date().getTime();
}
return Math.round(time / 1000)
},
} }

View File

@ -3,7 +3,7 @@
<div v-if="$Electron" class="common-app-down-link" @click="releasesNotification"> <div v-if="$Electron" class="common-app-down-link" @click="releasesNotification">
<Icon type="md-download"/> {{$L(repoTitle)}} <Icon type="md-download"/> {{$L(repoTitle)}}
</div> </div>
<a v-else class="common-app-down-link" :href="releases.html_url" target="_blank"> <a v-else class="common-app-down-link" :href="repoReleases.html_url" target="_blank">
<Icon type="md-download"/> {{$L(repoTitle)}} <Icon type="md-download"/> {{$L(repoTitle)}}
</a> </a>
</div> </div>
@ -15,7 +15,6 @@ import MarkdownPreview from "./MDEditor/components/preview";
import axios from "axios"; import axios from "axios";
Vue.component('MarkdownPreview', MarkdownPreview) Vue.component('MarkdownPreview', MarkdownPreview)
import {Store} from "le5le-store";
import {mapState} from "vuex"; import {mapState} from "vuex";
export default { export default {
@ -26,19 +25,19 @@ export default {
repoName: 'kuaifan/dootask', repoName: 'kuaifan/dootask',
repoData: {}, repoData: {},
repoStatus: 0, // 0 12
repoReleases: {},
status: 0, // 0 12 downloadResult: {},
releases: {},
downInfo: {},
} }
}, },
mounted() { mounted() {
this.getReleases(); this.getReleases();
// //
if (this.$Electron) { if (this.$Electron) {
this.$Electron.ipcRenderer.on('downloadDone', (event, args) => { this.$Electron.ipcRenderer.on('downloadDone', (event, {result}) => {
if (args.name == this.repoData.name) { if (result.name == this.repoData.name) {
this.downInfo = args; this.downloadResult = result;
this.releasesNotification() this.releasesNotification()
} }
}) })
@ -49,10 +48,10 @@ export default {
'wsOpenNum', 'wsOpenNum',
]), ]),
repoTitle() { repoTitle() {
return this.status == 2 ? '更新客户端' : '客户端下载'; return this.repoStatus == 2 ? '更新客户端' : '客户端下载';
}, },
showButton() { showButton() {
return this.status && !this.$store.state.windowMax768 && ['login', 'manage-dashboard'].includes(this.$route.name) return this.repoStatus && !this.$store.state.windowMax768 && ['login', 'manage-dashboard'].includes(this.$route.name)
} }
}, },
watch: { watch: {
@ -108,7 +107,7 @@ export default {
}, },
getReleases() { getReleases() {
if (this.status > 0) { if (this.repoStatus > 0) {
return; return;
} }
if (this.loadIng > 0) { if (this.loadIng > 0) {
@ -116,10 +115,11 @@ export default {
} }
// //
let cache = $A.getStorageJson("cacheAppdown"); let cache = $A.getStorageJson("cacheAppdown");
let timeout = 1800; let timeout = 600;
if (cache.time && cache.time + timeout > Math.round(new Date().getTime() / 1000)) { if (cache.time && cache.time + timeout > Math.round(new Date().getTime() / 1000)) {
this.releases = cache.data; this.repoReleases = cache.data;
this.chackReleases() this.chackReleases()
setTimeout(this.getReleases, timeout * 1000)
return; return;
} }
// //
@ -127,22 +127,24 @@ export default {
axios.get("https://api.github.com/repos/" + this.repoName + "/releases/latest").then(({status, data}) => { axios.get("https://api.github.com/repos/" + this.repoName + "/releases/latest").then(({status, data}) => {
this.loadIng--; this.loadIng--;
if (status === 200) { if (status === 200) {
$A.setStorage("cacheAppdown", { cache = {
time: Math.round(new Date().getTime() / 1000), time: Math.round(new Date().getTime() / 1000),
data: data data: data
}); }
this.releases = data; $A.setStorage("cacheAppdown", cache);
this.chackReleases(); this.repoReleases = cache.data;
setTimeout(this.getReleases, timeout) this.chackReleases()
} }
setTimeout(this.getReleases, timeout * 1000)
}).catch(() => { }).catch(() => {
this.loadIng--; this.loadIng--;
setTimeout(this.getReleases, timeout * 1000)
}); });
}, },
chackReleases() { chackReleases() {
let hostName = $A.getDomain(window.systemInfo.apiUrl); let hostName = $A.getDomain(window.systemInfo.apiUrl);
if (hostName == "" || hostName == '127.0.0.1') { if (hostName == "" || $A.leftExists(hostName, '127.0.0.1')) {
hostName = "public" hostName = "public"
} }
if (this.$Electron) { if (this.$Electron) {
@ -153,18 +155,18 @@ export default {
} }
let artifactName = null; let artifactName = null;
if (match[2] === 'darwin') { if (match[2] === 'darwin') {
artifactName = `${hostName}-${this.releases.tag_name}-mac-${match[3]}.pkg`; artifactName = `${hostName}-${this.repoReleases.tag_name}-mac-${match[3]}.pkg`;
} else if (match[2] === 'win32') { } else if (match[2] === 'win32') {
artifactName = `${hostName}-${this.releases.tag_name}-win-${match[3]}.exe`; artifactName = `${hostName}-${this.repoReleases.tag_name}-win-${match[3]}.exe`;
} else { } else {
return; return;
} }
this.repoData = (this.releases.assets || []).find(({name}) => name == artifactName); this.repoData = (this.repoReleases.assets || []).find(({name}) => name == artifactName);
if (!this.repoData) { if (!this.repoData) {
return; return;
} }
let currentVersion = window.systemInfo.version; let currentVersion = window.systemInfo.version;
let latestVersion = $A.leftDelete(this.releases.tag_name.toLowerCase(), "v") let latestVersion = $A.leftDelete(this.repoReleases.tag_name.toLowerCase(), "v")
if (this.compareVersion(latestVersion, currentVersion) === 1) { if (this.compareVersion(latestVersion, currentVersion) === 1) {
// //
console.log("New version: " + latestVersion); console.log("New version: " + latestVersion);
@ -174,26 +176,23 @@ export default {
} }
} else { } else {
// //
this.repoData = (this.releases.assets || []).find(({name}) => $A.strExists(name, hostName)); this.repoData = (this.repoReleases.assets || []).find(({name}) => $A.strExists(name, hostName));
if (this.repoData) { if (this.repoData) {
let latestVersion = $A.leftDelete(this.releases.tag_name.toLowerCase(), "v") let latestVersion = $A.leftDelete(this.repoReleases.tag_name.toLowerCase(), "v")
console.log("Exist client: " + latestVersion); console.log("Exist client: " + latestVersion);
this.status = 1; this.repoStatus = 1;
} }
} }
}, },
releasesNotification() { releasesNotification() {
if (this.downInfo.state != "completed") {
return;
}
$A.modalConfirm({ $A.modalConfirm({
okText: this.$L('立即更新'), okText: this.$L('立即更新'),
onOk: () => { onOk: () => {
this.installApplication(); this.installApplication();
}, },
onCancel: () => { onCancel: () => {
this.status = 2; this.repoStatus = 2;
}, },
render: (h) => { render: (h) => {
return h('div', { return h('div', {
@ -209,12 +208,12 @@ export default {
props: { props: {
color: 'volcano' color: 'volcano'
} }
}, this.releases.tag_name) }, this.repoReleases.tag_name)
]), ]),
h('MarkdownPreview', { h('MarkdownPreview', {
class: 'notification-body', class: 'notification-body',
props: { props: {
initialValue: this.releases.body initialValue: this.repoReleases.body
} }
}), }),
]) ])
@ -227,7 +226,7 @@ export default {
return; return;
} }
this.$Electron.ipcRenderer.send('openFile', { this.$Electron.ipcRenderer.send('openFile', {
path: this.downInfo.savePath path: this.downloadResult.savePath
}); });
this.$Electron.ipcRenderer.send('windowQuit'); this.$Electron.ipcRenderer.send('windowQuit');
} }

View File

@ -28,7 +28,8 @@
} }
.notification-body { .notification-body {
max-height: 210px; max-height: 210px;
overflow: auto; overflow-x: hidden;
overflow-y: auto;
margin: 18px 0; margin: 18px 0;
.markdown-preview { .markdown-preview {
margin: -20px -12px; margin: -20px -12px;