很差意思,鴿了挺久了,上一期咱們使用app.asar.unpacked
完成了electron的增量更新,本期咱們介紹的是如何使用exe替換asar
來實現增量更新。
本期內容是基於上一期的內容來說解的,且主要針對於Windows系統(mac系統能夠對app.asar修改)vue
asar
後,Windows系統的Electron應用啓動後其app.asar會被佔用,不能對其進行修改和刪除,必須結束Electron應用的進程後才能夠對其進行修改。其實呢子線程也能夠跑node,可是呢因爲主進程結束了咱們並無node環境運行node命令,因此此方法不通,固然你也可添加一個編譯好的node運行子線程js,可是體積問題得不償失。
在Windows下咱們能夠用批處理文件bat來處理文件,可是bat仍是有uac以及執行時會有cmd窗口,咱們能夠把寫好的bat文件轉換爲exe文件來解決這些問題。node
咱們這裏去除以前的app.asar.unpacked
打包,把以前vue.config的註釋掉,這樣咱們打包就只有asar文件了git
// extraResources: [{ // from: "dist_electron/bundled", // to: "app.asar.unpacked", // filter: [ // "!**/icons", // "!**/preload.js", // "!**/node_modules", // "!**/background.js" // ] // }], // files: [ // "**/icons/*", // "**/preload.js", // "**/node_modules/**/*", // "**/background.js" // ],
上一期構建增量zip時使用了afterPack鉤子,這裏咱們對其修改,把新版本的app.asar重命名爲update.asar
,放入增量的app.zip包裏,不瞭解的能夠看看上一期的內容github
const path = require('path') const AdmZip = require('adm-zip') const fse = require('fs-extra') exports.default = async function(context) { let targetPath if(context.packager.platform.nodeName === 'darwin') { targetPath = path.join(context.appOutDir, `${context.packager.appInfo.productName}.app/Contents/Resources`) } else { targetPath = path.join(context.appOutDir, './resources') } const asar = path.join(targetPath, './app.asar') fse.copySync(asar, path.join(context.outDir, './update.asar')) var zip = new AdmZip() zip.addLocalFile(path.join(context.outDir, './update.asar')) zip.writeZip(path.join(context.outDir, 'app.zip')) fse.removeSync(path.join(context.outDir, './update.asar')) }
同上一期,咱們這裏修改了upDateUrl和upDateExe,upDateUrl是增量zip,upDateExe是咱們用來替換asar的exe文件。vue-cli
{ "code": 200, "success": true, "data": { "forceUpdate": false, "fullUpdate": false, "upDateUrl": "http://127.0.0.1:4000/app.zip", "upDateExe": "http://127.0.0.1:4000/update.exe", "restart": false, "message": "我要升級成0.0.2", "version": "0.0.2" } }
這裏咱們再把加載策略修改成加載app.asar裏的文件shell
// createProtocol('app', path.join(resources, './app.asar.unpacked')) createProtocol('app')
這個沒變更,同上一期app
咱們把以前的主進程下載修改一下,先判斷update.exe
是否存在,不存在的話先下載update.exe
放入app.getPath('userData')
裏:electron
win:C:\Users\Administrator(你的用戶)\AppData\Roaming\<app name>\ mac:/Users/(你的用戶)/Library/Application Support/<app name>
app.getPath('userData')
這個路徑呢,比較特殊,安裝以後就存在了,是數據文件,你的indexDB,localStorage等都存在這裏面,軟件的全量更新,卸載都不會改動這個文件裏的東西,
咱們把update.exe
放入這裏,避免全量更新後從新安裝。以後下載增量更新包解壓到resourcesh中(update.asar),也就是和app.asar同級目錄,刪除zip包,運行app.exit(0)
關閉主進程async
import downloadFile from './downloadFile' import { app } from 'electron' const fse = require('fs-extra') const path = require('path') const AdmZip = require('adm-zip') export default async (data) => { const resourcesPath = process.resourcesPath if (!fse.pathExistsSync(path.join(app.getPath('userData'), './update.exe'))) { await downloadFile({ url: data.upDateExe, targetPath: app.getPath('userData') }) } downloadFile({ url: data.upDateUrl, targetPath: resourcesPath }).then(async (filePath) => { const zip = new AdmZip(filePath) zip.extractAllToAsync(resourcesPath, true, (err) => { if (err) { console.error(err) return } fse.removeSync(filePath) app.exit(0) }) }).catch(err => { console.log(err) }) }
主進程關閉後會觸發quit
事件,咱們在這個事件裏檢測update.exe
以及update.asar
是否同時存在,
同時存在的話咱們用spawn開啓一個子進程運行咱們的update.exe
,而且傳入resourcesPath
(app.asar所在目錄路徑),app.getPath('exe')
(咱們軟件的啓動路徑),
使用child.unref()
讓子進程和父進程分離,能夠不退出子進程的狀況下退出父進程。ui
const { spawn } = require('child_process') const fse = require('fs-extra') const fs = require('fs') const resourcesPath = process.resourcesPath app.on('quit', () => { console.log('quit') if (fse.pathExistsSync(path.join(app.getPath('userData'), './')) && fse.pathExistsSync(path.join(resourcesPath, './update.asar'))) { const logPath = app.getPath('logs') const out = fs.openSync(path.join(logPath, './out.log'), 'a') const err = fs.openSync(path.join(logPath, './err.log'), 'a') const child = spawn(`"${path.join(app.getPath('userData'), './update.exe')}"`, [`"${resourcesPath}"`, `"${app.getPath('exe')}"`], { detached: true, shell: true, stdio: ['ignore', out, err] }) child.unref() } })
也就是說這裏是父進程退出後,子進程執行咱們的exe,替換app.asar,out和err是將子進程執行的日誌重定向到app.getPath('logs')
中,這個路徑和electron-log
不同(你也能夠本身設置爲electron-log路徑同樣)
win:C:\Users\Administrator(你的用戶)\AppData\Roaming\<app name>\<app productName>\logs mac: ~/Library/Logs/<app name> ?應該是這個下面的,這個我沒驗證
準備工做完成了,這裏咱們編寫exe,其實這個沒啥難度的,咱們使用bat腳本打包成exe就行。
update.bat
@echo off timeout /T 1 /NOBREAK del /f /q /a %1\app.asar ren %1\update.asar app.asar start "" %2
簡單解釋一下吧,%1和%2爲運行腳本傳入的參數,好比update.bat aaa bbb
,那麼%1爲aaa,%2爲bbb,上面咱們用spawn運行exe時傳入的,
也就是%1爲resourcesPath,%2爲軟件的啓動exe,咱們運行bat腳本,先暫停1秒鐘保證主進程退出了,而後刪除app.asar,將update.asar重命名爲app.asar,啓動軟件exe。
一個簡單的bat替換就完成了,咱們下載Bat To Exe Converter這個軟件,將update.bat轉換爲update.exe,而後將update.exe放入咱們的http-server目錄中。運行軟件檢測更新,看看更新是否完成。
spawn(`"${path.join(app.getPath('userData'), './update.exe')}"`, [`"${resourcesPath}"`, `"${app.getPath('exe')}"`], { detached: true, shell: true, stdio: ['ignore', out, err] })
這裏有同窗可能會有疑問,爲何要在幾個路徑外加一個"",這是因爲node運行腳本的路徑名中包含空格的話,須要加上引號,bat處理也同樣,好比咱們的軟件安裝在c盤,C:\Program Files\electronVueDEV
,最多見的問題就是Program Files
這裏有個空格,這會致使bat命令裏有這樣的路徑的話會處理失敗,因此咱們的路徑都加了引號的。
本系列更新只有利用週末和下班時間整理,比較多的內容的話更新會比較慢,但願能對你有所幫助,請多多star或點贊收藏支持一下