From 218f2598d366c146ecad13166f041fe64f958e48 Mon Sep 17 00:00:00 2001 From: wxlong Date: Wed, 26 Nov 2025 14:53:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8B=E8=BD=BDGDB?= =?UTF-8?q?=E6=B2=A1=E6=9C=89ID=E5=AD=97=E6=AE=B5BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ExportController.java | 192 ++++++++++++++++-- .../utils/collection/StoreMediaFileUtil.java | 18 +- src/main/resources/application.properties | 16 +- 3 files changed, 206 insertions(+), 20 deletions(-) diff --git a/src/main/java/cn/edu/whu/boot/collection/controller/ExportController.java b/src/main/java/cn/edu/whu/boot/collection/controller/ExportController.java index 43743de..ecc4a34 100644 --- a/src/main/java/cn/edu/whu/boot/collection/controller/ExportController.java +++ b/src/main/java/cn/edu/whu/boot/collection/controller/ExportController.java @@ -44,6 +44,7 @@ import org.opengis.feature.simple.SimpleFeatureType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -76,6 +77,9 @@ public class ExportController{ private final UserService userService; private final ExportService exportService; + @Value("${gdal.proj.lib:}") + private String configuredProjLib; + @Autowired public ExportController(ITaskService taskService, IBaseService baseService, UserService userService, ExportService exportService){ this.taskService = taskService; @@ -301,16 +305,135 @@ public String taskTableDataExport(Integer taskId, String tableName, // } // return ResultMessage.success(null).toString(); //} + /** + * 初始化 GDAL/OGR 环境 + */ + private void initializeGdal() { + ogr.RegisterAll(); + gdal.AllRegister(); + // 支持中文路径和中文字段名 + gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); + gdal.SetConfigOption("SHAPE_ENCODING", ""); + + // 尝试设置 PROJ_LIB 以避免版本冲突 + // 优先级:配置文件 > 环境变量 > 自动查找 + String projLib = null; + + // 1. 首先检查配置文件中的设置 + if (StringUtils.isNotBlank(configuredProjLib)) { + File configProjDb = new File(configuredProjLib, "proj.db"); + if (configProjDb.exists()) { + projLib = configuredProjLib; + logger.info("使用配置文件中的 PROJ_LIB: {}", projLib); + } else { + logger.warn("配置文件中指定的 PROJ_LIB 路径不存在或未找到 proj.db: {}", configuredProjLib); + } + } + + // 2. 如果配置文件未设置,检查环境变量 + if (StringUtils.isBlank(projLib)) { + projLib = System.getenv("PROJ_LIB"); + if (StringUtils.isNotBlank(projLib)) { + File envProjDb = new File(projLib, "proj.db"); + if (envProjDb.exists()) { + logger.info("使用环境变量中的 PROJ_LIB: {}", projLib); + } else { + logger.warn("环境变量 PROJ_LIB 指定的路径未找到 proj.db: {}", projLib); + projLib = null; + } + } + } + + // 3. 如果都未设置,尝试从 Java 库路径自动查找 + if (StringUtils.isBlank(projLib)) { + String javaLibPath = System.getProperty("java.library.path"); + if (StringUtils.isNotBlank(javaLibPath)) { + String[] paths = javaLibPath.split(File.pathSeparator); + for (String path : paths) { + // 检查直接路径 + File projDbFile = new File(path, "proj.db"); + if (projDbFile.exists()) { + projLib = path; + logger.info("自动找到 PROJ 数据库路径: {}", path); + break; + } + // 检查 share/proj 子目录 + File projShareDir = new File(path, "share/proj"); + if (projShareDir.exists() && new File(projShareDir, "proj.db").exists()) { + projLib = projShareDir.getAbsolutePath(); + logger.info("自动找到 PROJ 数据库路径: {}", projLib); + break; + } + } + } + } + + // 4. 设置 PROJ_LIB(如果找到) + if (StringUtils.isNotBlank(projLib)) { + gdal.SetConfigOption("PROJ_LIB", projLib); + logger.info("已设置 PROJ_LIB: {}", projLib); + } else { + logger.warn("未找到有效的 PROJ_LIB 路径,将使用系统默认路径(可能遇到版本冲突)"); + } + } + + /** + * 创建空间参考系统(CGCS2000,EPSG:4490) + * 如果 ImportFromEPSG 失败,尝试使用 WKT 字符串 + */ + private SpatialReference createSpatialReference() { + SpatialReference srs = new SpatialReference(); + + // 首先尝试使用 EPSG 代码 + try { + int result = srs.ImportFromEPSG(4490); + if (result == 0) { + logger.debug("成功使用 EPSG:4490 创建空间参考"); + return srs; + } + logger.warn("EPSG:4490 导入返回错误码: {}", result); + } catch (Exception e) { + logger.warn("EPSG:4490 导入失败,异常: {}", e.getMessage()); + } + + // 如果失败,尝试使用 WKT 字符串(CGCS2000 的 WKT) + logger.info("尝试使用 WKT 字符串创建空间参考"); + try { + String wkt = "GEOGCS[\"CGCS2000\",DATUM[\"China_2000\",SPHEROID[\"CGCS2000\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]"; + int result = srs.ImportFromWkt(wkt); + if (result == 0) { + logger.info("成功使用 WKT 创建空间参考"); + return srs; + } + logger.warn("WKT 导入返回错误码: {}", result); + } catch (Exception e) { + logger.warn("WKT 导入失败,异常: {}", e.getMessage()); + } + + // 如果都失败,尝试使用 WGS84 作为备选(至少不会崩溃) + logger.warn("无法创建 CGCS2000 空间参考,使用 WGS84 (EPSG:4326) 作为备选"); + try { + srs = new SpatialReference(); + int result = srs.ImportFromEPSG(4326); + if (result == 0) { + logger.info("成功使用 WGS84 作为备选空间参考"); + return srs; + } + } catch (Exception e) { + logger.error("WGS84 导入也失败: {}", e.getMessage()); + } + + // 最后的备选:创建一个未定义的空间参考 + logger.error("所有空间参考创建方法都失败,返回未定义的空间参考"); + return new SpatialReference(); + } + /** * 下载数据接口,导出单个任务为gdb */ @RequestMapping(value = "taskGdbExport", produces = "text/html;charset=UTF-8") public String taskGdbExport(Integer taskId, HttpServletResponse response) throws IOException { - ogr.RegisterAll(); - gdal.AllRegister(); - // 支持中文路径和中文字段名 - gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); - gdal.SetConfigOption("SHAPE_ENCODING", ""); + initializeGdal(); Task task = taskService.getTaskById(taskId); if (task == null) return ResultMessage.failed("任务不存在").toString(); @@ -342,11 +465,7 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws public String batchGdbExport(@RequestParam List taskIds, HttpServletResponse response) throws IOException { if (taskIds == null || taskIds.isEmpty()) return ResultMessage.failed("任务ID列表为空").toString(); - ogr.RegisterAll(); - gdal.AllRegister(); - // 支持中文路径和中文字段名 - gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); - gdal.SetConfigOption("SHAPE_ENCODING", ""); + initializeGdal(); String exportDir = CommonConstant.TEMP_FILE_PATHNAME + "/" + System.currentTimeMillis(); File exportDirFile = new File(exportDir); @@ -452,8 +571,7 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws * 添加带几何的图层到 GDB(支持 Polygon 和 MultiPolygon) */ private void addGeometryLayerToGdb(DataSource dataSource, Table table, String layerName, List> dataList) { - SpatialReference srs = new SpatialReference(); - srs.ImportFromEPSG(4490); // CGCS2000 + SpatialReference srs = createSpatialReference(); // 使用改进的空间参考创建方法 // ✅ 直接从 JSON 中解析几何类型 int geomType = ogr.wkbPolygon; // 默认 Polygon @@ -487,6 +605,20 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws List fieldNameOrder = genFieldNameOrderStringArr(table, ""); if (fieldNameOrder == null || fieldNameOrder.isEmpty()) return; + String primaryKey = TableOperationUtil.getPrimaryKey(table); + + // ✅ 主键字段(默认整数型) + if (StringUtils.isNotBlank(primaryKey)) { + Field pkField = table.getFields().get(primaryKey); + FieldDefn pkDefn = new FieldDefn(primaryKey, ogr.OFTInteger); + if (pkField != null && StringUtils.isNotBlank(pkField.getName())) { + try { + pkDefn.SetAlternativeName(pkField.getName()); + } catch (Exception ignore) {} + } + layer.CreateField(pkDefn); + } + // ✅ 字段创建(英文名 + 中文别名) for (String fieldName : fieldNameOrder) { Field field = table.getFields().get(fieldName); @@ -503,6 +635,18 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws for (Map row : dataList) { Feature feature = new Feature(layer.GetLayerDefn()); + // 主键写入 + if (StringUtils.isNotBlank(primaryKey)) { + Object pkVal = row.get(primaryKey); + if (pkVal instanceof Number) { + feature.SetField(primaryKey, ((Number) pkVal).longValue()); + } else if (pkVal != null) { + feature.SetField(primaryKey, pkVal.toString()); + } else { + feature.SetFieldNull(primaryKey); + } + } + // 属性写入 for (String fieldName : fieldNameOrder) { Object val = row.get(fieldName); @@ -549,6 +693,19 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws return; } + if (StringUtils.isNotBlank(primaryKey)) { + Field pkField = table.getFields().get(primaryKey); + FieldDefn pkDefn = new FieldDefn(primaryKey, ogr.OFTInteger); + if (pkField != null && StringUtils.isNotBlank(pkField.getName())) { + try { + pkDefn.SetAlternativeName(pkField.getName()); + } catch (Exception e) { + System.err.println("⚠️ 设置主键别名失败:" + primaryKey + " → " + pkField.getName()); + } + } + layer.CreateField(pkDefn); + } + // ✅ 创建字段(英文名 + 中文别名) for (String fieldName : fieldNameOrder) { if (fieldName == null || fieldName.trim().isEmpty()) continue; @@ -570,6 +727,17 @@ public String taskGdbExport(Integer taskId, HttpServletResponse response) throws FeatureDefn featureDefn = layer.GetLayerDefn(); Feature feature = new Feature(featureDefn); + if (StringUtils.isNotBlank(primaryKey)) { + Object pkVal = row.get(primaryKey); + if (pkVal instanceof Number) { + feature.SetField(primaryKey, ((Number) pkVal).longValue()); + } else if (pkVal != null) { + feature.SetField(primaryKey, pkVal.toString()); + } else { + feature.SetFieldNull(primaryKey); + } + } + for (String fieldName : fieldNameOrder) { Object val = row.get(fieldName); feature.SetField(fieldName, val == null ? "" : val.toString()); diff --git a/src/main/java/cn/edu/whu/boot/common/utils/collection/StoreMediaFileUtil.java b/src/main/java/cn/edu/whu/boot/common/utils/collection/StoreMediaFileUtil.java index 5945d83..be9732f 100644 --- a/src/main/java/cn/edu/whu/boot/common/utils/collection/StoreMediaFileUtil.java +++ b/src/main/java/cn/edu/whu/boot/common/utils/collection/StoreMediaFileUtil.java @@ -122,7 +122,7 @@ public class StoreMediaFileUtil{ // return destFileName; // } public static String zip(String mediaPath, List filePathList, String password) { - String destFileName = mediaPath + "/temp.zip"; + String destFileName = mediaPath + File.separator + "temp.zip"; ZipParameters zipParam = new ZipParameters(); zipParam.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); zipParam.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); @@ -137,8 +137,20 @@ public class StoreMediaFileUtil{ ZipFile zipfile = new ZipFile(destFileName); for (String filePath : filePathList) { - File srcFile = new File(filePath.contains(CommonConstant.STATIC_RESOURCE_ROOT) ? filePath : mediaPath + filePath); - //File srcFile = new File(filePath); + File srcFile; + // 判断是否为绝对路径 + File pathFile = new File(filePath); + if (pathFile.isAbsolute()) { + // 绝对路径直接使用 + srcFile = pathFile; + } else if (filePath.contains(CommonConstant.STATIC_RESOURCE_ROOT)) { + // 包含静态资源根路径,直接使用 + srcFile = new File(filePath); + } else { + // 相对路径,需要拼接 mediaPath + srcFile = new File(mediaPath + File.separator + filePath); + } + if (!srcFile.exists()) { System.err.println("⚠️ 文件或目录不存在,跳过:" + srcFile.getAbsolutePath()); continue; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4947899..8f99bc1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,15 +1,15 @@ spring.profiles.active=dev -server.port=9001 +server.port=9006 server.address=0.0.0.0 spring.datasource.host=localhost spring.datasource.port=5432 -spring.datasource.database=tj_project +spring.datasource.database=eldc1125important spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://${spring.datasource.host}:${spring.datasource.port}/${spring.datasource.database}?useSSL=true&allowMultiQueries=true spring.datasource.username=postgres -spring.datasource.password=503503 +spring.datasource.password=123456 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=180000 spring.datasource.hikari.maximum-pool-size=30 @@ -33,7 +33,7 @@ logging.level.cn.edu.whu.boot.*.mapper=debug mybatis.mapper-locations=classpath:mapper/**/*.xml mybatis.configuration.map-underscore-to-camel-case=true -static.file.path=E:/503/TJ_project/static +static.file.path=C:/tj_lydc/static ApkFilePath=${static.file.path}/apk photozippath=${static.file.path}/zip @@ -71,4 +71,10 @@ spring.mail.properties.mail.smtp.port=465 spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.properties.mail.smtp.ssl.enable=true -email.subject= \ No newline at end of file +email.subject= + +# GDAL/PROJ 配置 +# PROJ_LIB 路径(可选,如果不设置则自动查找) +# 例如:gdal.proj.lib=E:/Java/jdk1.8.0_201/bin +# 或者:gdal.proj.lib=E:/Java/jdk1.8.0_201/bin/share/proj +gdal.proj.lib=C:/Java/gdal/bin/proj9/share \ No newline at end of file