使用Electron14.0.0打包java web服务为exe-爬坑记录
背景因为工作原因,需要实现一款让用户下载了exe,安装后,打开可视化界面即可自动启动java web服务,并将页面请求到首页服务。实现思路使用springboot做一个web应用,因为其内置了web容器,所以打包成jar后,通过java -jar即可启动web服务创建一个electron项目,把jar文件还有jre环境放进去通过nodejs的proceee提供的方法执行启动脚本(java -jar
·
背景
因为工作原因,需要实现一款让用户下载了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"
},
先写到这
- 写贴不易,转帖请标明来源
更多推荐
已为社区贡献1条内容
所有评论(0)