修复下载GDB没有ID字段BUG

This commit is contained in:
wxlong
2025-11-26 14:53:51 +08:00
parent f9064be39b
commit 218f2598d3
3 changed files with 206 additions and 20 deletions

View File

@@ -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 路径,将使用系统默认路径(可能遇到版本冲突)");
}
}
/**
* 创建空间参考系统CGCS2000EPSG: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<Integer> 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<Map<String, Object>> 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<String> 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<String, Object> 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());

View File

@@ -122,7 +122,7 @@ public class StoreMediaFileUtil{
// return destFileName;
// }
public static String zip(String mediaPath, List<String> 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;

View File

@@ -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=
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