Программирование

Как открыть HTML‑файл внутри app.asar в Electron

Узнайте, почему shell.openPath не открывает файлы внутри app.asar и как открыть HTML‑файл в браузере по умолчанию – во временную папку или используя протокол.

Как открыть HTML‑файл, упакованный в архив app.asar Electron‑приложения, через shell.openPath?
Я проверил существование файла с помощью

js
fs.existsSync('D:\Project\release\1.47.3\win-unpacked\resources\app.asar\dist\doc\index.html')

но вызов

js
shell.openPath('<path>')

не работает в Windows и выдаёт ошибку «cannot find the file», потому что ОС не может получить доступ к файловой системе asar.
Я хочу открыть файл в браузере по умолчанию, не копируя его в app.asar.unpacked. Как правильно открыть HTML‑файл, находящийся внутри app.asar?

Путь, который вы передаёте в shell.openPath, должен указывать на реальный файл, который ОС может прочитать.
app.asar – это просто один бинарный архив; ОС не рассматривает его как дерево каталогов, поэтому любой путь, указывающий внутрь архива, будет отклонён с ошибкой «не удаётся найти файл».

Из‑за этого ограничения «волшебного» способа вызвать shell.openPath с файлом внутри app.asar нет. На практике работают два привычных подхода:

Цель Что можно сделать
Открыть файл в браузере по умолчанию Скопировать файл во временную папку (или в app.asar.unpacked) и открыть реальный путь через shell.openExternal('file://…').
Серверить файл из приложения без извлечения Зарегистрировать собственный протокол (например, app://), который читает файл из архива, и открыть URL через shell.openExternal.

Ниже – конкретные примеры для обоих вариантов.


1. Скопировать в временный файл и открыть его

js
// main.js (main process)

const { app, shell } = require('electron');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { v4: uuidv4 } = require('uuid');   // npm i uuid

/**
 * Copy an HTML file that lives inside app.asar to a temp folder
 * and open it in the default browser.
 */
function openDocInBrowser() {
  // Where the file lives inside the asar
  const asarPath = path.join(process.resourcesPath, 'app.asar', 'dist', 'doc', 'index.html');

  // Create a temporary directory
  const tmpDir = path.join(os.tmpdir(), 'my-electron-app-docs');
  if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });

  // Destination path inside the temp directory
  const tmpFile = path.join(tmpDir, `index-${uuidv4()}.html`);

  // Copy the file out of the archive
  fs.copyFileSync(asarPath, tmpFile);

  // Open the temporary file in the default browser
  shell.openExternal(`file://${tmpFile}`);
}

// Call it at the appropriate time
app.whenReady().then(openDocInBrowser);

Почему это работает

  • fs.copyFileSync действительно работает, потому что API Node fs в Electron патчится так, чтобы читать файлы из архива asar.
  • Операция копирования создаёт реальный файл на диске.
  • shell.openExternal('file://…') передаёт абсолютный путь Windows, которая открывает его в браузере по умолчанию.

2. Показать файл через собственный протокол

Если хочется избежать записи на диск, можно открыть файл через собственный протокол и открыть URL.

js
// main.js

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

function createWindow () {
  const win = new BrowserWindow({ show: false });

  // Register a custom protocol that reads files from the asar archive
  protocol.registerFileProtocol('app', (request, callback) => {
    const url = request.url.substr(6); // strip 'app://'
    const filePath = path.join(process.resourcesPath, 'app.asar', url);
    callback({ path: filePath });
  });

  // Open the file using the custom protocol
  shell.openExternal('app://dist/doc/index.html');
}

app.whenReady().then(createWindow);

Пояснение

  • protocol.registerFileProtocol говорит Electron, что URL, начинающиеся с app://, – это запросы к файлам внутри архива asar.
  • Callback преобразует URL в реальный путь внутри app.asar, который Electron может прочитать.
  • shell.openExternal('app://…') передаёт URL ОС; ОС откроет его в браузере, потому что схема URL распознаётся как ресурс, похожий на файл.

Итог

  • shell.openPath не может открыть файл внутри app.asar – ОС не читает файловую систему архива.
  • Практичные решения:
    1. Скопировать файл во временную папку (или в app.asar.unpacked) и открыть его через shell.openExternal('file://…').
    2. Показать файл через собственный протокол (app://…) и открыть URL через shell.openExternal.

Выбирайте подход, который лучше вписывается в вашу стратегию развертывания: для быстрого «открыть в браузере» проще использовать временный файл; если нужно полностью избежать записи на диск, используйте собственный протокол.

Авторы
Проверено модерацией
Модерация