SpringBoot框架自动发布GeoSever图层服务(实战,包会!!!)

1. 工作空间

工作空间可以看作是包含数据的容器

2. 存储仓库

存储仓库是数据的具体存放地

3.发布图层名

图层名是发布服务的名称

4.springboot发布逻辑

4.1发布需要前端传的参数

1.压缩包:一个包含.shp,.shx和.dbf的zip压缩包(必要参数)
2.发布图层名(可选参数)

4.2发布需要前端传的参数规范

1.压缩包需要里面不包含文件夹(即解压一级目录直接是.shp等文件)
2.压缩包和里面的内部文件如.shp,.shx,.dbf等文件需要名称一致!
3.java发布图层服务的时候,尽量不要用中文名,因为发布的图层会拼接一个http的url,因为编码报错。我们项目的思路是将服务名为时间戳(确保了唯一),将中文名和服务名字时间戳变成一条记录,插入到mysql

5.项目实战发布逻辑

5.1GeoServerController

package com.ruoyi.gis.controller;

import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.gis.common.Result;
import com.ruoyi.gis.dto.GeoServerDTO;
import com.ruoyi.gis.model.Demo;
import com.ruoyi.gis.model.YzghyMajorProjectGisfile;
import com.ruoyi.gis.service.YzghyMajorProjectGisfileService;
import com.ruoyi.gis.utlis.GeoserverUtils;
import com.ruoyi.gis.utlis.ShpToGeoJsonConverter;
import com.ruoyi.gis.utlis.ZipExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @BelongsProject: geoserver-manager
 * @BelongsPackage: com.mr.xi.enums
 * @Author: cuiYong
 * @CreateTime: 2024-03-20  17:12
 * @Description: TODO
 * @Version: 1.0
 */


@RestController
@RequestMapping("/geoserver")
public class GeoServerController {

    //    private static final String GEOSERVER_DATA_DIR = "http://20.14.52.236/geoserverdata/E:\\geoserverdata\\data";
    private static final String GEOSERVER_DATA_DIR = "E:\\geoserverdata\\data";
//    private static final String GEOSERVER_DATA_DIR = "E:\\geoserverdata\\data";

    @Autowired
    private YzghyMajorProjectGisfileService yzghyMajorProjectGisfileService;

    @Anonymous
    @PostMapping("/publishLayerService")
    public Result publishLayerService(@RequestParam("layerName") String layerName,
                                      @RequestParam("zipFile") MultipartFile zipFile, @RequestParam("author") String author) throws IOException {

        String mysqlLayerName =layerName;
        layerName = GeoserverUtils.getCurrentTimeStamp();
        if (zipFile.isEmpty()) {
            throw new IllegalArgumentException("zip文件未上传");
        }
        String originalFileName = zipFile.getOriginalFilename();
        if (originalFileName != null && !originalFileName.isEmpty()) {
            String fileSuffix = originalFileName.substring(originalFileName.lastIndexOf(".") + 1).toLowerCase();
            if (!fileSuffix.equals("zip")) {
                return Result.fail(400, "您上传的是" + fileSuffix + "文件,请上传压缩包zip文件");
            }
        } else {
            return Result.fail(400, "无法获取上传文件的名称");
        }
        String zipFileName = zipFile.getOriginalFilename();
        if (zipFileName == null) {
            return Result.fail(400, "zip文件名获取失败");
        }

//        查询zip是否已经转为geojson或者是否已经发布过了
        if (zipFile.getSize() < 5 * 1024 * 1024) {
            Integer result = yzghyMajorProjectGisfileService.checkNameExistence(mysqlLayerName, "geojson");
            System.out.println("result===>"+result);
            if (result != null && result > 0) {
                return Result.fail(601, "该相同名称的文件已存在,请重新起一个新的文件名称");
            }
        } else {
            Integer result = yzghyMajorProjectGisfileService.checkNameExistence(mysqlLayerName, "geoserver");
            if (result != null && result > 0) {
                return Result.fail(601, "该相同名称的GeoServer服务已存在,请重新起一个新的文件名称");
            }
        }

        String directoryPath = GEOSERVER_DATA_DIR + "\\" + layerName;
        String temporary = directoryPath + File.separator + GeoserverUtils.getCurrentTimeStamp();
        File directoryTime = new File(temporary);
        if (!directoryTime.exists() && !directoryTime.mkdirs()) {
            return Result.fail(500, "无法创建目录: " + temporary);
        }
        File shpTempFile = new File(temporary, layerName + ".zip");

        zipFile.transferTo(shpTempFile);
        // temporary 临时文件夹路径,shpTempFile 是 ZIP 文件
        File outputFolder = new File(temporary); // 如果temporary是临时路径,在那一层解压
        try {
            ZipExtractor.unzipShpFile(shpTempFile, outputFolder, layerName);
            ZipExtractor.rezipShapeFiles(outputFolder, shpTempFile, layerName);
        } catch (IllegalArgumentException e) {
            // 这里捕获了具体的 IllegalArgumentException 异常,并提供了一个明确的错误消息
            System.out.println(e.getMessage());
            return Result.fail(500, "解压ZIP文件失败:文件可能损坏或格式不正确。");
        } catch (IOException e) {
            // 其他 I/O 异常的错误处理
            e.printStackTrace();
            return Result.fail(500, "处理ZIP文件时出现I/O异常:" + e.getMessage());
        }

        YzghyMajorProjectGisfile yzghyMajorProjectGisfile = new YzghyMajorProjectGisfile();
        yzghyMajorProjectGisfile.setName(mysqlLayerName);
        yzghyMajorProjectGisfile.setOwner(author);
        yzghyMajorProjectGisfile.setCoordinate("4326");
        yzghyMajorProjectGisfile.setCreateTime(LocalDateTime.now());
        yzghyMajorProjectGisfile.setDescription("");


        if (zipFile.getSize() < 5 * 1024 * 1024) { // 小于5MB
            System.out.println("temporary + File.separator + layerName + "+temporary + File.separator + layerName + ".shp");
            System.out.println(GEOSERVER_DATA_DIR + File.separator + "geojson" + File.separator + mysqlLayerName + ".geojson");

            ShpToGeoJsonConverter.transformShpToGeoJson(temporary + File.separator + layerName + ".shp", "F:\\fileServer\\geojson\\" + mysqlLayerName + ".geojson");

            GeoserverUtils.deleteDirectoryWithContents(directoryTime);
            yzghyMajorProjectGisfile.setType("geojson");
            yzghyMajorProjectGisfile.setUrl("geojson" + "/" + mysqlLayerName + ".geojson");
            Integer result = yzghyMajorProjectGisfileService.insertGeoServerInfo(yzghyMajorProjectGisfile);
            if (result == 0) {
                return Result.fail(500, "插入数据库失败");
            }
            yzghyMajorProjectGisfile.setId(yzghyMajorProjectGisfile.getId());
            return Result.success(yzghyMajorProjectGisfile);
        } else {

            publishToGeoserver(temporary, layerName);
            yzghyMajorProjectGisfile.setType("geoserver");
            yzghyMajorProjectGisfile.setUrl("ghy:"+layerName);
            Integer result = yzghyMajorProjectGisfileService.insertGeoServerInfo(yzghyMajorProjectGisfile);
            if (result == 0) {
                return Result.fail(500, "插入数据库失败");
            }
            yzghyMajorProjectGisfile.setId(yzghyMajorProjectGisfile.getId());
            return Result.success(yzghyMajorProjectGisfile);
        }

    }
    private void publishToGeoserver(String directoryPath, String layerName) throws IOException {
        String geoserverUrl = "http://localhost:8888/geoserver";
        String workspace = "ghy";
        String storeName = "yzghy";
        String srs = "EPSG:4326";
        File zipShpFile = new File(directoryPath, layerName + ".zip");
        GeoserverUtils.GeoserverPublishShapefileData(geoserverUrl, "admin", "geoserver",
                workspace, storeName, srs, zipShpFile, layerName, "file:data/" + layerName + "/" + layerName + ".shp");
    }

}


大致逻辑为:大于5mb的文件发布GeoServer服务,小于5mb的文件转为geojson保存到指定目录

细分逻辑:
1.先判断上传的zip格式是否合法,如:是否为zip文件\是否为空等
2.不过zip里面是否包含文件夹,或压缩包内的.shp,.shx,.dbf与zip不同名(我这里的发布服务的名字按照时间戳来 确保唯一)都给他转为当前时间戳
3.发布服务或转geojson文件,事先在mysql数据库查明,是否已经发布过了,逻辑很简单,举个例子:那前端传过来的layerName去数据库查询这个名字在不在就行,这里的代码就不贴了
4.判断zip是否大于5mb,小于则转为geojson保存到指定目录,大于则发布geoserver服务(其中不管是转geojson还是发布geoserver服务 我都会忘数据库插入命令记录,sql语句是insert into ghy_major_project_gisfile values('${name}','${owner}','${url}','${description}','${coordinate}','${createTime}','${type}'))

5.2shp转geoJson工具类

package com.ruoyi.gis.utlis;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geojson.feature.FeatureJSON;
import org.opengis.feature.simple.SimpleFeatureType;



public class ShpToGeoJsonConverter {
    public static boolean transformShpToGeoJson(String shpPath, String geojsonPath) {
        FileDataStore myData = null;
        try {
            File file = new File(shpPath);
            myData = FileDataStoreFinder.getDataStore(file);
            // 设置解码方式
            ((ShapefileDataStore) myData).setCharset(StandardCharsets.UTF_8);
            SimpleFeatureSource source = myData.getFeatureSource();
            SimpleFeatureType schema = source.getSchema();
            Query query = new Query(schema.getTypeName());

            SimpleFeatureCollection collection = source.getFeatures(query);
            FeatureJSON fjson = new FeatureJSON();

            try (StringWriter writer = new StringWriter()) {
                writer.write("{\"type\":\"FeatureCollection\",\"crs\":");
                fjson.writeCRS(schema.getCoordinateReferenceSystem(), writer);
                writer.write(",\"features\":");
                fjson.writeFeatureCollection(collection, writer);
                writer.write("}");


                try (BufferedWriter buffer = Files.newBufferedWriter(new File(geojsonPath).toPath(), StandardCharsets.UTF_8)) {
                    buffer.write(writer.toString());
                }
                return true;
            }
        } catch (IOException e) {
            return false;
        } finally {
            if (myData != null) {
                // 关闭FileDataStore释放资源
                myData.dispose();
            }
        }
    }



}

5.3zip解压缩工具类(这个工具很强大!)

去除zip中的文件夹,只保留文件/并且将压缩包内的文件夹去除后,文件名字与zip统一后保留压缩包

package com.ruoyi.gis.utlis;

import java.io.*;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipOutputStream;

public class ZipExtractor {
    private static final Set<String> ALLOWED_FILE_EXTENSIONS = new HashSet<>(Arrays.asList("shp", "shx", "dbf"));



    private static String getFileExtension(String fileName) {
        int lastIndex = fileName.lastIndexOf('.');
        if (lastIndex > -1) {
            return fileName.substring(lastIndex + 1).toLowerCase();
        }
        return "";
    }

    private static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
        File destinationFile = new File(destinationDir, zipEntry.getName());

        String destDirPath = destinationDir.getCanonicalPath();
        String destFilePath = destinationFile.getCanonicalPath();

        if (!destFilePath.startsWith(destDirPath + File.separator)) {
            throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
        }

        return destinationFile;
    }

    private static void moveValidFilesAndCleanup(File directory, File outputFolder, String layerName) throws IOException {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 递归调用,处理子目录
                    moveValidFilesAndCleanup(file, outputFolder, layerName);
                } else {
                    String fileExtension = getFileExtension(file.getName());
                    if (ALLOWED_FILE_EXTENSIONS.contains(fileExtension)) {
                        // 根据layerName重命名文件。例如: "my_layer.shp"
                        File renamedFile = new File(outputFolder, layerName + "." + fileExtension);
                        if (!file.renameTo(renamedFile)) {
                            throw new IOException("Failed to move and rename file: " + file.getName());
                        }
                    }
                }
            }
        }

        // 删除临时目录/文件夹
        deleteDirectory(directory);
    }

    // 递归删除目录
    private static void deleteDirectory(File directory) throws IOException {
        File[] files = directory.listFiles();

        if (files != null) {
            // 删除目录中的所有文件和子目录
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    if (!file.delete()) {
                        throw new IOException("Failed to delete file: " + file.getName());
                    }
                }
            }
        }

        // 删除当前目录
        if (!directory.delete()) {
            throw new IOException("Failed to delete directory: " + directory.getName());
        }
    }
    public static void unzipShpFile(File zipFile, File outputFolder, String layerName) throws IOException {
        // 解压缩到临时目录,防止覆盖其他文件
        File tempDirectory = new File(outputFolder, "temp_unzip");
        tempDirectory.mkdirs(); // 确保临时目录存在

        byte[] buffer = new byte[1024];
        // 使用指定字符集创建ZipInputStream
        ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile), Charset.forName("GBK"));
        ZipEntry zipEntry = zis.getNextEntry();


        while (zipEntry != null) {
            File newFile = newFile(tempDirectory, zipEntry);

            if (!zipEntry.isDirectory() && ALLOWED_FILE_EXTENSIONS.contains(getFileExtension(newFile.getName()))) {
                File parent = newFile.getParentFile();
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException("Failed to create directory " + parent);
                }

                try (FileOutputStream fos = new FileOutputStream(newFile)) {
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                }
            }
            zipEntry = zis.getNextEntry();
        }

        zis.closeEntry();
        zis.close();

        // 移动有效文件到输出目录,并重命名,然后删除临时目录
        moveValidFilesAndCleanup(tempDirectory, outputFolder, layerName);
    }

    public static void rezipShapeFiles(File inputFolder, File originalZipFile, String layerName) throws IOException {
        File[] shapeFiles = inputFolder.listFiles((dir, name) -> ALLOWED_FILE_EXTENSIONS.contains(getFileExtension(name)));

        if (shapeFiles == null || shapeFiles.length == 0) {
            throw new IOException("No valid shape files found to zip in directory: " + inputFolder.getPath());
        }

        // 删除原始的zip文件
        if (originalZipFile.exists() && !originalZipFile.delete()) {
            throw new IOException("Failed to delete original zip file: " + originalZipFile.getName());
        }

        // 创建新的Zip文件
        File zipOutputFile = new File(inputFolder, layerName + ".zip");

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipOutputFile))) {
            byte[] buffer = new byte[1024];

            for (File shapeFile : shapeFiles) {
                try (FileInputStream fis = new FileInputStream(shapeFile)) {
                    // 创建一个新的ZipEntry,不要包含任何父路径
                    ZipEntry zipEntry = new ZipEntry(shapeFile.getName());
                    zos.putNextEntry(zipEntry);

                    int length;
                    while ((length = fis.read(buffer)) > 0) {
                        zos.write(buffer, 0, length);
                    }
                }

                zos.closeEntry();
            }
        }
    }
}

上效果图:
zip工具类已经将这个zip格式规范化,只要他zip有这三个文件,我通通都给他规范化,保你发布服务成功!(保留了shp等文件)
在这里插入图片描述

5.4核心工具GeoServerUtils

代码非常细致,考虑了很多隐藏错误,都解决了!!!
发布GeoServer服务的核心工具:记住几个传的参数就行了很简单,这里我简单说一下这几个参数(String url, String username,String passwd, String ws,String storeName, String srs, File zipFile, String layerName, String urlDatastore):

1 url:geoserver的服务地址和端口
2.username:geoserver的登录用户名
3.passwd:geoserver的登录密码
4.ws:工作区名称
5.storeName:存储仓库名称
6.srs:标记坐标(这个坐标是不具备转坐标的功能,只能说是标记一下文件的坐标,需要自己手动写死)
7.zipFile:包含.shp,.shx,.dbf等shapefile的必要文件的zip(我前面zip工具类已经将这个zip格式规范化,只要他zip有这三个文件,我通通都给他规范化,保你发布服务成功!)
8.layerName:发布图层名称(再说一句,发布图层名和zip和shp的名称都需统一,我用的是当前时间戳作为名称统一的)
9.urlDatastore:shp的地址(我当时也在纳闷,都有包含shp的zip了为什么还需要指定shp的地址/不过我的zip都给他规范好了)

package com.ruoyi.gis.utlis;

import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.decoder.RESTDataStore;
import it.geosolutions.geoserver.rest.decoder.RESTLayer;
import it.geosolutions.geoserver.rest.encoder.datastore.GSShapefileDatastoreEncoder;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Slf4j
public class GeoserverUtils {


    public static void main(String[] args) throws IOException {
        //GeoServer的连接配置
        String url = "http://localhost:8888/geoserver";
        String username = "admin";
        String passwd = "geoserver";

        String ws = "testshape";     //待创建和发布图层的工作区名称workspace
        String storeName = "testShapeStore1"; //待创建和发布图层的数据存储名称store
        String srs = "EPSG:4528";
        //压缩文件的完整路径
        File zipFile = new File("E:/geoserverdata/data/shapefile/cs.zip");
        String layerName = "cs";//图层名称
        //shp文件所在的位置
        String urlDatastore = "file:data/shapefile/c123s.shp";
        GeoserverPublishShapefileData(url, username,
                passwd, ws,
                storeName, srs,
                zipFile, layerName, urlDatastore);
    }

    //发布shapefile数据
    public static void GeoserverPublishShapefileData(String url, String username,
                                                     String passwd, String ws,
                                                     String storeName, String srs,
                                                     File zipFile, String layerName, String urlDatastore) throws IOException {
        URL u = new URL(url);
        //获取管理对象
        GeoServerRESTManager manager = new GeoServerRESTManager(u, username, passwd);
        //获取发布对象
        GeoServerRESTPublisher publisher = manager.getPublisher();
        //获取所有的工作空间名称
        List<String> workspaces = manager.getReader().getWorkspaceNames();
        //判断工作空间是否存在
        if (!workspaces.contains(ws)) {
            //创建一个新的存储空间
            boolean createws = publisher.createWorkspace(ws);
            System.out.println("create ws : " + createws);
        } else {
            System.out.println("workspace已经存在了,ws :" + ws);
        }
        //判断数据存储(datastore)是否已经存在,不存在则创建
        URL urlShapefile = new URL(urlDatastore);
        RESTDataStore restStore = manager.getReader().getDatastore(ws, storeName);
        if (restStore == null) {
            //创建shape文件存储
            GSShapefileDatastoreEncoder store = new GSShapefileDatastoreEncoder(storeName, urlShapefile);
            boolean createStore = manager.getStoreManager().create(ws, store);
            System.out.println("create store : " + createStore);
        } else {
            System.out.println("数据存储已经存在了,store:" + storeName);
        }

        //判断图层是否已经存在,不存在则创建并发布
        RESTLayer layer = manager.getReader().getLayer(ws, layerName);
        if (layer == null) {
            //发布图层
            boolean publish = manager.getPublisher().publishShp(ws, storeName, layerName, zipFile, srs);

            System.out.println("publish : " + publish);
        } else {

            System.out.println("图层已经存在了" + layerName);
        }
    }


    public static  String getCurrentTimeStamp() {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return now.format(formatter);
    }
    // 添加一个用于递归删除文件夹及其所有内容的辅助方法
    public static void deleteDirectoryWithContents(File directory) {
        File[] allContents = directory.listFiles();
        if (allContents != null) {
            for (File file : allContents) {
                deleteDirectoryWithContents(file);
            }
        }
        if (!directory.delete()) {
            System.out.println("警告:无法删除临时文件夹 " + directory.getAbsolutePath());
        }
    }

}



最后只有俩sql方法没贴了:
一个sql是查询数据库有没有用这个layerName发布的服务或geojson
一个是插入发布记录
都是很简单的SQL
下面是我给的一些建议,大家在借鉴我的代码时可以一个方法一个方法测试,如果有不懂的可以私信我,也可以在评论区留意,我会回复滴。祝大家工作顺利!!谢谢

  • 37
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要实现Spring Boot与GeoServer的整合,需要进行以下步骤: 1. 配置GeoServer 首先需要安装并配置好GeoServer。可以从GeoServer的官网下载安装包,然后按照官方文档进行配置。 2. 创建一个Spring Boot工程 使用Spring Initializr创建一个Spring Boot工程,并添加必要的依赖,例如Spring Web和Spring Boot DevTools等。 3. 添加GeoServer的REST API依赖 在pom.xml文件中添加GeoServer的REST API依赖: ```xml <dependency> <groupId>org.geoserver</groupId> <artifactId>gs-restconfig</artifactId> <version>2.17.2</version> </dependency> ``` 4. 配置GeoServer的REST API客户端 在Spring Boot的配置文件中,配置GeoServer的REST API客户端: ```yaml geoserver: rest: endpoint: http://localhost:8080/geoserver/rest username: admin password: geoserver ``` 其中,endpoint是GeoServer的REST API的地址,username和password是GeoServer的管理员账号和密码。 5. 创建一个发布服务 创建一个发布服务,将数据发布到GeoServer。可以使用GeoServer的REST API来完成这个过程。以下是一个简单的实现示例: ```java @Service public class LayerPublishService { @Autowired private GeoServerRESTPublisher publisher; public void publishLayer() throws Exception { // 创建一个数据存储 String workspaceName = "test"; String storeName = "test-store"; URL url = new URL("file:///path/to/shapefile"); DataStoreInfo store = new DataStoreInfo(url, storeName); store.setEnabled(true); store.setType(DataStoreInfo.Type.SHAPEFILE); publisher.createWorkspace(workspaceName); publisher.publishDatastore(workspaceName, store); // 创建一个层 String layerName = "test-layer"; String styleName = "Default"; String typeName = "test-store:test-layer"; publisher.publishFeatureType(workspaceName, storeName, layerName, typeName, styleName, null); } } ``` 需要注意的是,这里的GeoServerRESTPublisher是由gs-restconfig依赖提供的,可以通过Spring的依赖注入来获取。 6. 测试发布服务 在Spring Boot的控制器中,添加一个测试发布服务的接口: ```java @RestController public class TestController { @Autowired private LayerPublishService layerPublishService; @GetMapping("/publishLayer") public String publishLayer() throws Exception { layerPublishService.publishLayer(); return "success"; } } ``` 启动Spring Boot应用,并访问http://localhost:8080/publishLayer,即可测试发布服务。 以上就是整合Spring Boot和GeoServer发布层的步骤。需要注意的是,这里只是一个简单的示例,实际上还需要进行更多的配置和优化才能投入生产环境。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a勇学java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值