You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写跨平台Electron asar更新器?解决Linux权限难题

跨平台Electron ASAR文件更新器实现问题(无需Root权限)

问题描述

我想做一个仅下载最新app.asar并替换旧文件的Electron更新器,不想使用electron-updater这类主要针对Windows和Mac的方案,要实现支持Linux在内所有平台的「ASAR更新器」,逻辑和Discord更新器类似:对比本地版本与服务器最新版本。

但Linux下多数包格式会将文件挂载在需要Root权限的目录,比如AppImage挂载到/tmp下的随机目录,Flatpak挂载到/app/lib/(暂未找到该目录),核心问题是:如何无需Root权限替换旧的asar文件为更新后的版本?

测试代码

app.whenReady().then(() => {
    // Updater should only be run in "production"
    if (isDev) return;
    // Get path of asar file & resource dir stored on the system
    const asarPath = app.getAppPath(); // => /tmp/.APPNAMExlsjda345/resources/app.asar/src/electron/
    const resourcesPath = path.join(asarPath, ".."); // => /tmp/.APPNAMExlsjda345/resources/

    // TODO: compare current version with newest on webserver
    // TODO: download new version & call it something like "update.asar"
    // TODO: remove app.asar & rename update.asar to app.asar
    // TODO: restart the whole app

    // This is only for testing if I can modify the content of the resources directory
    fs.copyFile(asarPath, path.join(resourcesPath, "update.asar"), (err) => {
      if (err) return console.log({ err }); // throws error, see below!
      console.log("copied: ", fs.readdirSync(resourcesPath)); // if it would be successfull print all the content of resources dir
    });
  }
});

错误信息

使用fs.copyFile时的错误

{
  err: Error: ENOENT,  not found in /tmp/.mount_Babbel2t0KoE/resources/app.asar
      at createError (node:electron/js2c/asar_bundle:5:1255)
      at Object.t.<computed> [as copyFile] (node:electron/js2c/asar_bundle:5:2526)
      at /tmp/.mount_Babbel2t0KoE/resources/app.asar/src/electron/main.js:44:6 {
    code: 'ENOENT',
    errno: -2
  }
}

尝试original-fs后的错误

{
  err: [Error: EROFS: read-only file system, copyfile '/tmp/.mount_BabbelMjuGx3/resources/app.asar' -> '/tmp/.mount_BabbelMjuGx3/resources/update.asar'] {
    errno: -30,
    code: 'EROFS',
    syscall: 'copyfile',
    path: '/tmp/.mount_BabbelMjuGx3/resources/app.asar',
    dest: '/tmp/.mount_BabbelMjuGx3/resources/update.asar'
  }
}

解决方案

核心问题原因

AppImage/Flatpak这类Linux包格式会将应用挂载为只读文件系统,这是安全设计,防止运行时篡改应用文件,所以你无法直接修改挂载目录内的app.asar。

可行实现方案

1. 将更新的asar存储到用户可写目录

不要直接修改挂载目录的文件,而是:

  • 获取用户专属可写目录:用app.getPath('userData'),这个路径在所有平台都可写,无需Root权限。
  • 版本对比与下载:检查本地版本和服务器版本,确认更新后,将新asar下载到该用户目录,命名为app-updated.asar。
  • 启动加载优先级:应用启动时优先检查用户目录是否存在更新后的asar,存在则加载该文件,否则加载默认挂载目录的app.asar。

2. 启动加载逻辑示例

const { app, BrowserWindow } = require('electron');
const path = require('path');
const fs = require('fs');

// 用户目录下的更新asar路径
const updatedAsarPath = path.join(app.getPath('userData'), 'app-updated.asar');
// 默认的asar路径
const defaultAsarPath = app.getAppPath();

// 优先加载更新后的asar
let loadPath = defaultAsarPath;
if (fs.existsSync(updatedAsarPath)) {
  loadPath = updatedAsarPath;
}

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(loadPath, 'src', 'electron', 'preload.js') // 根据项目结构调整路径
    }
  });
  mainWindow.loadFile(path.join(loadPath, 'index.html'));
}

app.whenReady().then(() => {
  if (!isDev) {
    // 执行版本检查与下载逻辑
    // 1. 请求服务器获取最新版本号
    // 2. 对比本地版本(可从package.json或用户目录配置文件读取)
    // 3. 需更新则下载新asar到updatedAsarPath
    // 4. 下载完成后提示用户重启应用
  }
  createWindow();

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit();
});

3. 应用重启处理

下载完成更新的asar后,提示用户重启应用,用app.relaunch() + app.quit()实现,重启后应用会加载用户目录内的新asar。

4. Flatpak兼容注意事项

Flatpak环境下,app.getPath('userData')指向~/.var/app/[你的应用ID]/config/[应用名],该目录完全可写,无需特殊权限,按上述逻辑处理即可,无需手动查找/app/lib目录。

优化点

  • 下载时添加文件校验(如MD5/SHA256),确保文件完整。
  • 若更新失败,保留旧更新文件,下次启动重新下载。
  • 启动时若更新asar加载失败,自动回退到默认asar,避免应用无法启动。

内容的提问来源于stack exchange,提问作者wjatscheslaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 03:03:22