将Geoserver服务打包到Tauri程序中使用

GeoServer在Tauri中无感启动与使用

吃力不讨好的事情 PC端加载至少1G的tif文件到地图中展示
离谱
切片都不帮个忙
这不难为老实人么


甲、环境Tauri跨端程序

前端

  • ✅\] 得升级`@tauri-apps/cli`到`>=2.0.0-beta.0`

rust

  • ✅\] 得升级`tauri-build`到`2.0.0-beta.10`


乙、下载最新的Geoserver二进制文件

一定🏳️

一定🏳️

一定🏳️

下载二进制文件Platform Independent Binary

Geoserver Platform Independent Binary


丙、 下载最低17版本的java

一定🏳️

一定🏳️

一定🏳️

下载免安装解压即用的

java_17 zip


丁、 设置Tauri 打包文件

tauri.conf.json

json 复制代码
 "resources": [
      "./resources/geoserver-main-latest-bin"
 ]

文件geoserver-main-latest-bin中需要包含下载解压的java_17 最终项目结构是

-src-tauri/resources/geoserver-main-latest-bin

--bin

--data_dir

--etc

--jdk_17

--lib

--license

--licenses

--log

--modules

--resources

--webapps

--start.ini

--start.jar

--VERSION.txt


戍、rust执行终端命令唤起geoserver

1) 使用std::env::current_dir判断文件是否存在
rust 复制代码
    let binding = env::current_dir().unwrap().join("resources/geoserver-main-latest-bin");
    let flag = is_path_exists(binding.to_str().unwrap());
2) 使用std::process::Command执行终端命令
rust 复制代码
    //CREATE_NO_WINDOW 叮嘱此变量是设置终端命令执行时不创建终端窗口(仅限window)
    let binding = env::current_dir().unwrap().join("resources/geoserver-main-latest-bin");
    let java_path = binding.join(r"jdk_17/bin/java.exe");
    let jar_path = binding.join(r".\start.jar");
    const CREATE_NO_WINDOW: u32 = 0x08000000;
    let  command  = Command::new(java_path)
        .arg("-jar")
        .arg(jar_path)
        .current_dir(binding)
        .creation_flags(CREATE_NO_WINDOW)
        .spawn();
3) 使用std::net::UdpSocket::bind获取当前设备IP
rust 复制代码
fn get_local_ip() -> Option<IpAddr> {
    let socket = match std::net::UdpSocket::bind("0.0.0.0:0") {
        Ok(socket) => socket,
        Err(_) => return None,
    };

    match socket.connect("8.8.8.8:80") {
        Ok(()) => (),
        Err(_) => return None,
    }

    socket.local_addr().ok().map(|addr| addr.ip())
}

fn is_port_available(ip: IpAddr, port: u16) -> bool {
    let addr = SocketAddr::new(ip, port);
    TcpStream::connect(addr).is_ok()
}

#[tauri::command]
pub async fn get_localhost_ip() -> Result<CustomResult> {
    if let Some(ip) = get_local_ip() {
        println!("Local IP address: {}", ip);

        let port = 28080;
        if is_port_available(ip, port) {
            return Ok(CustomResult::new(true, 200, "Success", Some(json!({
                "ip": ip,
                "port": port
            }))));
        } else {
            return Err(CustomResult::new(false, 500, &format!("Port {} is not available", port), None));
        }
    } else {
        return Err(CustomResult::new(false, 500, "Failed to get local IP address", None));
    }
}

CustomResult是我自定义的内容返回格式,请自行处理


己、唤起服务

不在项目启动时唤起,单独开启子进程会阻塞主进程

在前端初始化时在进行IPC通信唤起

设置打开Geoserver时单独唤起一个webview

ts 复制代码
import { invokeGeoServerExist, invokeGeoServerStart, invokeGetLocalhostIp } from "@/ipc-apps/invoke.geoserver";
import { ResultMeta } from "@/types/result";
import { useEffect, useState } from "react";
import { WebviewWindow  } from '@tauri-apps/api/webviewWindow';

export const useGeoServerClient = () => {
    const [isExist, setExist] = useState<boolean>(false);

    const [isStart, setStart] = useState<boolean>(false);

    const [ipAddress, setIpAddress] = useState<{
        address: string,
        msg: string
    }>({
        address: '',
        msg: 'GeoServer服务未启动'
    })

    const queryGeoServer = async () => {
        invokeGeoServerExist().then((res: ResultMeta) => {
            console.log('是否存在 = ',res.success);
            if(res.success){
                setExist(true);
                invokeGeoServerStart().then((res: ResultMeta) => {
                    console.info('geoserver启动成功');
                    // res.success && setStart(true);
                })
            }
        })
    }
    const geoserverUrl = (_isStart: boolean, uri: string) => {
        if(!_isStart)return;
        const webview = new WebviewWindow("geoserver-window", {
            url: `${uri}/geoserver/index.html`,
            x: 0,
            y: 0,
            width: 1440,
            height: 1080,
            title: "GeoServer自启服务",
          });
        webview.once('tauri://created', function () {
        // webview 窗口成功创建
        console.log('成功');
        
        });

        webview.once('tauri://error', function (e) {
        // 创建窗口时出现错误
        console.log('错误, ', e);
        
        });
    }

    useEffect(() => {
        console.log("是否启动 = ", isExist);
        
        if(isExist){
            const _timer = setInterval(() => {
                invokeGetLocalhostIp().then(res => {
                    console.log('res =', res);
                    
                    if(res.success){
                        setStart(true)
                        const { 
                            ip, port
                        } = res.payload;
                        setIpAddress({
                            address: `http://${ip}:${port}`,
                            msg: 'GeoServer服务已启动'
                        })
                        clearInterval(_timer)
                    }else{
                        setIpAddress({
                            address: "",
                            msg: res.msg
                        })
                    }
                })
            }, 1000)
        }
    }, [isExist])



    return {
        isStart,
        ipAddress,
        isExist,
        queryGeoServer,
        geoserverUrl
    }
}

庚、得设置允许http使用

tauri.conf.json

json 复制代码
"security": {
      "csp": null
    },
相关推荐
祈澈菇凉2 小时前
解释什么是受控组件和非受控组件
前端·javascript·react.js
理查der驾2 小时前
mini-react 第八天:做一个TODO 应用
react.js
Yeauty3 小时前
三分钟掌握视频分辨率修改 | 在 Rust 中优雅地使用 FFmpeg
rust·ffmpeg·音视频
Java水解5 小时前
【一起学Rust | Tauri2.0框架】基于 Rust 与 Tauri 2.0 框架实现软件开机自启
java·后端·rust
情非得已小猿猿6 小时前
‌React Hooks主要解决什么
javascript·react.js·ecmascript
zhyoobo7 小时前
现代前端开发框架对比:React、Vue 和 Svelte 的选择指南
前端·vue.js·react.js
Source.Liu8 小时前
【CXX】6.10 *mut T,*const T原始指针
c++·rust·cxx
Hello.Reader8 小时前
Rust + WebAssembly 实现康威生命游戏
游戏·rust·wasm
一只小松许️8 小时前
Rust语言介绍和猜数字游戏的实现
开发语言·游戏·rust
无名之逆9 小时前
使用 Hyperlane 框架的 WebSocket 功能
服务器·前端·网络·websocket·网络协议·http·rust