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 = "",
devloadCachePath = path.resolve(__dirname, ".devload"),
downloadList = [],
downloadCacheFile = path.join(app.getPath('cache'), config.name + '.downloadCache');
downloadCacheFile = path.join(app.getPath('cache'), config.name, '.downloadCache');
if (fs.existsSync(devloadCachePath)) {
devloadUrl = fs.readFileSync(devloadCachePath, 'utf8')
@ -23,6 +23,39 @@ if (fs.existsSync(downloadCacheFile)) {
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() {
mainWindow = new BrowserWindow({
width: 1280,
@ -69,37 +102,12 @@ function createMainWindow() {
})
mainWindow.webContents.session.on('will-download', (event, item) => {
item.setSavePath(path.join(app.getPath('cache'), item.getFilename()));
item.on('done', (event, state) => {
try {
const info = {
state,
name: item.getFilename(),
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) { }
item.setSavePath(path.join(app.getPath('cache'), config.name, item.getFilename()));
item.on('updated', () => {
downloadUpdate(item)
})
item.on('done', () => {
downloadUpdate(item)
})
})
}
@ -201,26 +209,33 @@ ipcMain.on('inheritClose', (event) => {
* @param args {url}
*/
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"
//
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);
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">
<Icon type="md-download"/> {{$L(repoTitle)}}
</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)}}
</a>
</div>
@ -15,7 +15,6 @@ import MarkdownPreview from "./MDEditor/components/preview";
import axios from "axios";
Vue.component('MarkdownPreview', MarkdownPreview)
import {Store} from "le5le-store";
import {mapState} from "vuex";
export default {
@ -26,19 +25,19 @@ export default {
repoName: 'kuaifan/dootask',
repoData: {},
repoStatus: 0, // 0 12
repoReleases: {},
status: 0, // 0 12
releases: {},
downInfo: {},
downloadResult: {},
}
},
mounted() {
this.getReleases();
//
if (this.$Electron) {
this.$Electron.ipcRenderer.on('downloadDone', (event, args) => {
if (args.name == this.repoData.name) {
this.downInfo = args;
this.$Electron.ipcRenderer.on('downloadDone', (event, {result}) => {
if (result.name == this.repoData.name) {
this.downloadResult = result;
this.releasesNotification()
}
})
@ -49,10 +48,10 @@ export default {
'wsOpenNum',
]),
repoTitle() {
return this.status == 2 ? '更新客户端' : '客户端下载';
return this.repoStatus == 2 ? '更新客户端' : '客户端下载';
},
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: {
@ -108,7 +107,7 @@ export default {
},
getReleases() {
if (this.status > 0) {
if (this.repoStatus > 0) {
return;
}
if (this.loadIng > 0) {
@ -116,10 +115,11 @@ export default {
}
//
let cache = $A.getStorageJson("cacheAppdown");
let timeout = 1800;
let timeout = 600;
if (cache.time && cache.time + timeout > Math.round(new Date().getTime() / 1000)) {
this.releases = cache.data;
this.repoReleases = cache.data;
this.chackReleases()
setTimeout(this.getReleases, timeout * 1000)
return;
}
//
@ -127,22 +127,24 @@ export default {
axios.get("https://api.github.com/repos/" + this.repoName + "/releases/latest").then(({status, data}) => {
this.loadIng--;
if (status === 200) {
$A.setStorage("cacheAppdown", {
cache = {
time: Math.round(new Date().getTime() / 1000),
data: data
});
this.releases = data;
this.chackReleases();
setTimeout(this.getReleases, timeout)
}
$A.setStorage("cacheAppdown", cache);
this.repoReleases = cache.data;
this.chackReleases()
}
setTimeout(this.getReleases, timeout * 1000)
}).catch(() => {
this.loadIng--;
setTimeout(this.getReleases, timeout * 1000)
});
},
chackReleases() {
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"
}
if (this.$Electron) {
@ -153,18 +155,18 @@ export default {
}
let artifactName = null;
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') {
artifactName = `${hostName}-${this.releases.tag_name}-win-${match[3]}.exe`;
artifactName = `${hostName}-${this.repoReleases.tag_name}-win-${match[3]}.exe`;
} else {
return;
}
this.repoData = (this.releases.assets || []).find(({name}) => name == artifactName);
this.repoData = (this.repoReleases.assets || []).find(({name}) => name == artifactName);
if (!this.repoData) {
return;
}
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) {
//
console.log("New version: " + latestVersion);
@ -174,26 +176,23 @@ export default {
}
} 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) {
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);
this.status = 1;
this.repoStatus = 1;
}
}
},
releasesNotification() {
if (this.downInfo.state != "completed") {
return;
}
$A.modalConfirm({
okText: this.$L('立即更新'),
onOk: () => {
this.installApplication();
},
onCancel: () => {
this.status = 2;
this.repoStatus = 2;
},
render: (h) => {
return h('div', {
@ -209,12 +208,12 @@ export default {
props: {
color: 'volcano'
}
}, this.releases.tag_name)
}, this.repoReleases.tag_name)
]),
h('MarkdownPreview', {
class: 'notification-body',
props: {
initialValue: this.releases.body
initialValue: this.repoReleases.body
}
}),
])
@ -227,7 +226,7 @@ export default {
return;
}
this.$Electron.ipcRenderer.send('openFile', {
path: this.downInfo.savePath
path: this.downloadResult.savePath
});
this.$Electron.ipcRenderer.send('windowQuit');
}

View File

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