electron 增量更新【替换asar】

893 阅读1分钟

关闭应用后替换asar的exe

//update.bat -> update.exe【Bat To Exe Converter】
@echo off
timeout /T 1 /NOBREAK
del /f /q /a %1\app.asar
ren %1\update.asar app.asar
start "" %2

升级部分

//update.js
const fs = require("fs")
const path = require('path')
const fse = require('fs-extra')
const AdmZip = require("adm-zip");
const { spawn } = require('child_process')
const { app, Notification } = require('electron')

//asar文件所在位置
const asarPath = path.join(__dirname, "../../")

/**
 * 启动更新
 * @param {*} url 获取版本信息的地址 http://xxxx......
 * @param {*} version 当前版本 x.x.x
 */
module.exports.updated = (url, version) => {

    //appData应用文件夹
    const appPath = path.join(app.getPath('userData'))

    //请求版本信息
    fetch(url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
    }).then(
        (response) => response.json()
    ).then((res) => {
        /**
         * {
         *    version: "x.x.x",
         *    url:"http://xxxx......"
         * }
         */
        const newVersion = res.version
        if (newVersion === version) return
        info("应用更新中")
        const updateUrl = res.url
        //获取版本文件
        fetch(updateUrl, {
            method: "GET",
            headers: {
                "responseType": 'arraybuffer',
            },
        })
            .then(res => res.blob())
            .then(async blob => {
                const buffer = Buffer.from(await blob.arrayBuffer());
                const zipPath = path.join(appPath, "update.zip")
                fs.writeFile(zipPath, buffer, (err) => {
                    if (!err) {
                        let exeName = "update.exe"
                        let exePath = path.join(appPath, exeName)
                        const exeFromPath = path.join(__dirname, exeName)//升级器存放位置
                        //更新升级器
                        fs.copyFile(exeFromPath, exePath, () => { })
                        const zip = new AdmZip(zipPath);
                        //解压版本文件到目的地
                        zip.extractAllTo(asarPath, true);
                        //关闭应用
                        setTimeout(()=>{
                            app.exit(0)
                        },2000)
                    }
                });
            });
    }).catch((error) => {
        console.error("UPDATE-ERR:", error);
    });
}

/**
 * 监听应用关闭
 */
app.on('quit', () => {
    const updateAsarPath = path.join(asarPath, "update.asar")
    //如果更新文件存在
    if (fse.pathExistsSync(path.join(app.getPath('userData'), './')) && fse.pathExistsSync(updateAsarPath)) {
        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')
        //启动更新器替换asar文件
        const child = spawn(`"${path.join(app.getPath('userData'), './update.exe')}"`, [`"${asarPath}"`, `"${app.getPath('exe')}"`], {
            detached: true,
            shell: true,
            stdio: ['ignore', out, err]
        })
        child.unref()
    }
})

const info = (msg) => {
    const NOTIFICATION_TITLE = ''
    const NOTIFICATION_BODY = msg

    new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
}

main.js

//main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
let win
const { updated } = require("./update")

const version = "0.0.2"

const createWindow = () => {
    win = new BrowserWindow({
        frame: true,//关闭原生导航栏
        width: 1000,
        height: 580,
        title: "xxxx", //默认窗口标题 -最低优先级
        backgroundColor: "#fff", //窗口背景色 默认-#FFF
        autoHideMenuBar: true,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false, //上下文隔离
        }
    })
    //判断环境 开发环境 or 生产环境
    if (process.env.NODE_ENV != 'development') {
        win.loadFile(path.join(__dirname, "../dist/index.html"))
    } else {
        win.loadURL(`${process.env['VITE_DEV_SERVER_URL']}`)
    }
    updated("http://127.0.0.1:5000/uodate", version)
}

app.whenReady().then(createWindow)

打包的时候生成zip


/**
 * 打包的时候将app.asar复制一份,改名成update.asar并压缩(update.zip)
 */

const path = require("path")
const fs = require("fs")
const archiver = require('archiver');


const zipFile = (sourceFile, destZip) => {
    let output = fs.createWriteStream(destZip);
    let archive = archiver('zip', {
        zlib: { level: 9 }
    });
    archive.pipe(output);
    archive.file(sourceFile, {
        name: path.basename(sourceFile)
    });
    archive.finalize();
}

const asarPath = path.join(__dirname, "./release/win-unpacked/resources","app.asar")
const upDataAsarPath = path.join(__dirname, "./release/win-unpacked/resources","update.asar")
const upDataAsarZipPath = path.join(__dirname, "./release","update.zip")

fs.copyFile(asarPath,upDataAsarPath,(err)=>{
    if(!err){
        zipFile(upDataAsarPath, upDataAsarZipPath)
    }
})

传送门

[大佬文章](Electron增量更新(二) - 陌路凡歌 electron 前端的技术分享与个人见解 (xuxin123.com))

[全量更新]# 🐱‍🐉Electron-🐱‍🏍远程更新-🐱‍👤electron-updater-🎉全量更新🤳