使用Electron14.0.0打包java web服務為exe-爬坑記錄


背景

因為工作原因,需要實現一款讓用戶下載了exe,安裝后,打開可視化界面即可自動啟動java web服務,並將請求到首頁的應用。

實現思路

  • 使用springboot做一個web應用,因為其內置了web容器,所以打包成jar后,通過java -jar即可啟動web服務
  • 創建一個electron項目,把jar文件還有jre運行環境放進去
  • 通過nodejs的process提供的方法執行啟動腳本(java -jar xxx.jar)
  • electron 渲染進程實現服務是否已啟動的狀態檢測,如果已經啟動直接訪問首頁,如果沒有啟動則執行上一步的啟動腳本

1、基於springboot構建的web服務

  • 直接網上找了一個現成的-若依(非前后端分離版本)
  • 然后clone代碼,根據文檔,完成本地環境的搭建,能實現把服務打成jar(mvn install)
  • 使用java -jar xx.jar命令測試啟動是否正常
  • 測試首頁能否訪問
  • 一切正常

2、創建一個electron項目

  • electron官網有一個electron-quick-star項目,可以參考。
  • clone下來,安裝依賴(指定國內鏡像地址)
  • 安裝依賴前可以把鏡像源修改到國內,要不然等待是個很熬人的過程
  • npm install --registry=https://registry.npm.taobao.org // 使用淘寶鏡像下載(一次有效)
  • npm config set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron // 把electron鏡像也換一下,我記得有一個100多m的exe文件需要從這里下載。
  • 運行起來(npm start)

3、在項目根目錄下創建java應用文件夾

在這里插入圖片描述

  • app文件夾下放了業務系統jar文件
  • jre是java1.8的運行環境

4、編寫啟動命令

  • 啟動命令使用了 const { spawn, exec } = require(‘child_process’)
  • electron 不建議在主進程里做業務操作,所以打算把啟動命令放到渲染進程里
  • 但是渲染進程已經不支持使用require進行nodejs Api引用了,查了好久,網上給的建議是將nodeIntegration為true,但是根據我的測試,此處修改成true也不行,然后我又測試了一下electron的9.4.4版本,發現可行。看來是最新的版本取消了這個屬性的作用,同時官網提供了新的解題思路:
    在這里插入圖片描述- 首先在main.js里提供了預加載屬性,並且提供了一個preload.js的調用
  • 在preload.js里可以require Node.js APIs
  • preload.js 聲明的屬性及function可以通過contextBridge暴露給渲染進程
  • 上代碼:preload.js:
const { contextBridge } = require('electron')
const { spawn, exec } = require('child_process')
const path = require('path')

contextBridge.exposeInMainWorld('myAPI', {
  startServerForSpawn: () => {
    let path1 = path.join(__dirname, 'app/ruoyi-admin.jar');
    const ls = spawn('java', ['-jar', path1]);
    ls.stdout.on('data', (data) => {
      if(data.toString().indexOf("Started RuoYiApplication") !== -1){
        window.location.href="http://localhost:80";
      }
    });
    ls.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
      alert("啟動服務異常");
    });
    ls.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
    });
  },
  startServerForbat: () =>{
    const bat = spawn(path.join(__dirname, 'my.bat'));
    bat.stdout.on('data', (data) => {
      console.log(data.toString());
      if(data.toString().indexOf("Started RuoYiApplication") !== -1){
        window.location.href="http://localhost:80";
      }
    });
    bat.stderr.on('data', (data) => {
      console.error(data.toString());
    });
    bat.on('exit', (code) => {
      console.log(`Child exited with code ${code}`);
    });
  }
})
  • 在渲染進程 loading.html 頁面里調用(使用window對象調用)
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"> -->
    <title>loading</title>
  </head>
  <body>
    <h1>Loading</h1>
    <div>正在啟動,請等待!</div>
    <script> window.myAPI.startServerForbat(); console.log("-----------hello-------------") </script>
  </body>
</html>

  • 在preload.js里有兩個方法實現了java -jar命令的執行:startServerForSpawn、startServerForbat
  • 其實都是使用spawn函數進行執行,不過一個是直接執行命令,一個是執行一個bat文件
  • 經過測試我發現第一種在啟動了java web服務之后,如果把當前頁面跳轉到其他頁面(window.location.href)后,子進程就自動銷毀了,web服務也隨之關閉了。
  • 而使用bat就沒有這個問題,甭管你是關閉了渲染進程還是主進程都不會影響web服務,在下次啟動exe的時候也就不用再重新啟動web服務了,直接訪問,大大減少了啟動時間。
  • my.bat腳本:
cd ./jre/bin
java -jar ../../app/ruoyi-admin.jar

5、編寫主頁面判斷邏輯-index.html

  • 應用起來后先成本地加載index.html文件
  • index.html內通過訪問本地服務驗證服務是否開啟(過期時間200)
  • 如果已開啟,直接跳轉到服務首頁
  • 未啟動,則加載上一步的loading頁面,啟動服務
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="">
    <title>index</title>
  </head>
  <body>
    <!-- We are using Node.js <span id="node-version"></span>, Chromium <span id="chrome-version"></span>, and Electron <span id="electron-version"></span>. -->
   
    <script src="./static/js/jquery.min.js"> </script>
    <script> $(document).ready(function(){ $.ajax({ timeout: 200, type: 'GET', url: 'http://localhost', data: { }, success: function(obj){ window.location.href = "http://localhost" }, error: function(obj){ window.location.href = "loading.html"; } }) }) </script>
  </body>
</html>
  • 這里用的是jquery庫發ajax請求驗證
  • 本來打算使用nodejs的http模塊的get方法進行測試,但是發現如果服務器沒有啟動,根本走不到回調里去(我在回調了做了statusCode的判斷、以及綁定了data、end、error事件,均沒有執行)
  • 還試了electron的net模塊訪問,也是不走回調,所以放棄之。
  • 訪問首頁的時候發現業務系統的console有報錯,好像是對方前端庫使用了jquery導致的。所以在main.js里設置了一個屬性,解決了問題:contextIsolation:true

6、打包 electron-builder

  • 直接上package.json,貼上就能用,如果本地沒有安裝electron-builder,npm run dist時先加載依賴
{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "package": "electron-packager . construction --win --out build --arch=x64 --version1.0.0 --overwrite --icon=static/images/128.ico",
    "dist": "electron-builder --win --x64",
    "win32": "electron-builder --win --ia32"
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "^14.0.0"
  },
  "build": {
    "appId": "com.phil.test",
    "copyright": "https://github.com/phil-cheng",
    "productName": "java打包",
    "asar": false,
    "mac": {
        "target": [
            "dmg",
            "zip"
        ]
    },
    "win": {
        "target": [
            "nsis",
            "zip"
        ],
        "icon": "static/images/256.ico"
    }
}
}

  • 看上述scipts:打windows 64位 exe 執行npm run dist;32位執行npm run win32

注意:

  • 因為第一次上述配置里"asar"默認為true,所以打包會把應用下的代碼打包成一個歸檔文件-asar,如圖右邊,這就會導致程序在執行bat腳本時找不到本地文件。
    在這里插入圖片描述

  • asar嚴格意義上也不是對代碼加密,只是類似於zip一樣做了歸檔處理,通過其對應的命令是可以“解壓”出來的

  • 解決辦法有兩個:

    • 第一個就是把asar設置成false,不歸檔
    • 第二個是在打包的時候把本地文件copy到包外邊(例如:bat、jar、sqllite數據庫等本地文件),此辦法沒有驗證過,可以參考這個
"extraResources":  { //把需要訪問的文件移動到外層目錄
     "from": "template",
     "to": "temp"
 },

先寫到這

  • 寫貼不易,轉帖請標明來源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM