應用程式打包

為了減少圍繞著 Windows 上長路徑名稱問題的 issues ,稍微地加速 require 和隱藏你的原始碼避免不小心被看到,你可以選擇把你的應用程式打包成一個 asar 壓縮檔,只需要改變一點點你的原始碼就好。

產生 asar 壓縮檔

一個 asar 壓縮檔是一個簡單的類 tar 格式的檔案,它會把幾個檔案串接成一個檔案, Electron 可以不需要解壓縮整個檔案就從中讀取任意檔案。

把你的應用程式打包成 asar 壓縮檔的步驟:

1. 安裝 asar 工具包

$ npm install -g asar

2. 用 asar pack 打包

$ asar pack your-app app.asar

使用 asar 壓縮檔

在 Electron 中有兩組 API:Node.js 提供的 Node APIs 和 Chromium 提供的 Web APIs,兩組 API 都支援從 asar 壓縮檔中讀取檔案。

Node API

因為 Electron 中有一些特別的補釘,像是 fs.readFilerequire 這樣的 Node API 會將 asar 壓縮檔視為許多虛擬目錄,將裡頭的檔案視為在檔案系統上的一般檔案。

例如,假設我們有一個 example.asar 壓縮檔在 /path/to 中:

$ asar list /path/to/example.asar
/app.js
/file.txt
/dir/module.js
/static/index.html
/static/main.css
/static/jquery.min.js

讀取一個在 asar 壓縮檔中的檔案:

const fs = require('fs');
fs.readFileSync('/path/to/example.asar/file.txt');

列出所有在壓縮檔根目錄下的檔案:

const fs = require('fs');
fs.readdirSync('/path/to/example.asar');

使用一個壓縮檔中的模組:

require('/path/to/example.asar/dir/module.js');

你也可以利用 BrowserWindowasar 壓縮檔中呈現一個網頁:

const BrowserWindow = require('electron').BrowserWindow;
var win = new BrowserWindow({width: 800, height: 600});
win.loadURL('file:///path/to/example.asar/static/index.html');

Web API

在一個網頁中,壓縮檔中的檔案都可以透過 file: 這個協定被存取,如同 Node API,asar 壓縮檔都被視為目錄。

例如,要透過 $.get 取得一個檔案:

<script>
var $ = require('./jquery.min.js');
$.get('file:///path/to/example.asar/file.txt', function(data) {
  console.log(data);
});
</script>

把一個 asar 壓縮檔視為一般檔案

在一些像是驗證 asar 壓縮檔檢查碼(checksum)的例子中,我們需要以檔案的方式讀取 asar 壓縮檔中的內容,為了達到這個目的,你可以使用內建的 original-fs 模組,它提供了沒有 asar 支援的原生 fs API:

var originalFs = require('original-fs');
originalFs.readFileSync('/path/to/example.asar');

你也可以設定 process.noAsartrue 來關掉在 fs 模組中的 asar 支援:

process.noAsar = true;
fs.readFileSync('/path/to/example.asar');

Node API 上的限制

儘管我們盡可能的努力嘗試著使 Node API 中的 asar 壓縮檔像目錄一樣運作,還是有一些基於 Node API 低階本質的限制存在。

壓縮檔都是唯讀的

所有壓縮檔都無法被修改,因此所有可以修改檔案的 Node API 都無法與 asar 壓縮檔一起運作。

使用中的目錄無法被設為壓縮檔中的目錄

儘管 asar 壓縮檔被視為目錄,卻並沒有真正的目錄在檔案系統中,所以你永遠無法將使用中的目錄設定成 asar 壓縮檔中的目錄,把他們以 cwd 選項的方式傳遞,對某些 API 也會造成錯誤。

更多透過 API 拆封的方法

大部分 fs API 可以讀取一個檔案,或是不用拆封就從 asar 壓縮檔中取得一個檔案的資訊,但對一些需要傳遞真實檔案路徑給現行系統呼叫的 API ,Electron 將會解開需要的檔案到一個暫時的檔案,然後傳遞該暫時檔案的路徑給那些 API 以便使他們可以運作,這會增加這些 API 一點負擔。

需要額外拆封的 API :

  • child_process.execFile
  • child_process.execFileSync
  • fs.open
  • fs.openSync
  • process.dlopen - 在原生模組中被 require 使用

fs.stat 的不真實的狀態資訊

fs.stat 回傳的 Stats 物件和它在 asar 壓縮檔中的檔案朋友都是以猜測的方式產生的,因為那些檔案不存在檔案系統,所以你不應該信任 Stats 物件,除了取得檔案大小和確認檔案型態之外。

執行 asar 壓縮檔中的二進位檔

有像是 child_process.execchild_process.spawnchild_process.execFile 的 Node APIs 可以執行二進位檔,但只有 execFileasar 壓縮檔中可以執行二進位檔的。

這是因為 execspawn 接受的輸入是 command 而不是 file,而 command 們都是在 shell 底下執行,我們找不到可靠的方法來決定是否一個命令使用一個在 asar 壓縮檔中的檔案,而儘管我們找得到,我們也無法確定是否我們可以在沒有外部影響(side effect)的情況下替換掉命令中的路徑。

加入拆封檔案到 asar 壓縮檔中

如前述,一些 Node API 再被呼叫時會拆封檔案到檔案系統,除了效能議題外,它也會導致掃毒軟體發出 false alerts。

要繞過這個問題,你可以透過使用 --unpack 選向來拆封一些建立壓縮檔的檔案,以下是一個不包含共享原生模組的函式庫的例子:

$ asar pack app app.asar --unpack *.node

執行這個命令以後,除了 app.asar 以外,還有一個帶有拆封檔案的 app.asar.unpacked 資料夾被產生出來,當你要發布給使用者時,你應該把它和 app.asar 一起複。