GeoServer服务3个必要参数介绍(Java版)
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
下面是我给的一些建议,大家在借鉴我的代码时可以一个方法一个方法测试,如果有不懂的可以私信我,也可以在评论区留意,我会回复滴。祝大家工作顺利!!谢谢