GDB 文件导入流程分析

接口概述

基本信息

  • 接口功能: 导入 ZIP 文件(包含地理空间数据 GBD 格式文件)
  • 接口说明: 导入包含水储存量元数据的 ZIP 文件,支持多图层 GDB 格式解析

请求参数

json 复制代码
{
  "file": MultipartFile,        // ZIP 格式文件(必填)
  "overwriteMode": Boolean    // 覆盖模式(可选,默认 false)
}

依赖关系

复制代码
Controller → Service → Utils → GDAL/GeoTools → Database
    ↓            ↓         ↓           ↓
Swagger    ServiceImpl  FileParser   Entity
           ↑           ↑           ↑
           Mapper      FileHandleMapper

详细流程分析

第1步:文件接收与验证

控制器处理
java 复制代码
@PostMapping("/biz/skscclysj/importZip")
public CommonResult<Map<String, Object>> importZip(
    @RequestParam("file") MultipartFile file,
    @RequestParam(value = "overwriteMode", defaultValue = "false") Boolean overwriteMode) {
    
    return CommonResult.data(dbSkscclysjService.importZipFile(file, overwriteMode));
}

处理逻辑:

  1. 接收 MultipartFile 参数(ZIP 格式)
  2. 调用服务层处理文件
  3. 返回处理结果

第2步:服务层处理

核心流程
java 复制代码
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> importZipFile(MultipartFile file, Boolean overwriteMode) {
    Map<String, Object> result = new HashMap<>();
    List<String> errorList = new ArrayList<>();
    Map<String, Integer> layerCounts = new HashMap<>();
    int totalCount = 0;
    int successCount = 0;
    int errorCount = 0;
    
    // 1. 创建临时目录
    String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "dbyts_import_" + IdUtil.simpleUUID();
    Path tempPath = Paths.get(tempDir);
    
    // 2. 文件格式校验
    String originalFilename = file.getOriginalFilename();
    if (ObjectUtil.isEmpty(originalFilename) || !originalFilename.toLowerCase().endsWith(".zip")) {
        throw new CommonException("文件格式错误,仅支持ZIP格式文件");
    }
    
    // 3. 解压ZIP文件
    FileHandleUtils.unzipFile(file, tempPath);
    
    // 4. 查找GDB文件
    List<File> gdbFiles = FileHandleUtils.findGdbFiles(tempPath.toFile());
    if (gdbFiles.isEmpty()) {
        throw new CommonException("ZIP文件中未找到GDB格式文件");
    }
    
    // 5. 解析每个GDB文件
    for (File gdbFile : gdbFiles) {
        GbdFileParser.MultiLayerGdbResult gdbResult = GbdFileParser.parseMultiLayerGdbWithGdal(gdbFile, "dbyts");
        
        // 6. 处理每个图层的数据
        for (String layerName : gdbResult.getLayerNames()) {
            List<?> layerData = gdbResult.getLayerDataMap().get(layerName);
            int layerSuccess = processLayerData(layerName, layerData, overwriteMode, errorList);
            layerCounts.put(layerName, layerSuccess);
            totalCount += layerData.size();
            successCount += layerSuccess;
            errorCount += (layerData.size() - layerSuccess);
        }
    }
    
    // 7. 返回结果
    result.put("totalCount", totalCount);
    result.put("successCount", successCount);
    result.put("errorCount", errorCount);
    result.put("layerCounts", layerCounts);
    result.put("errorDetail", errorList);
    
    return result;
}

第3步:文件处理工具 (FileHandleUtils)

文件解压功能
java 复制代码
public static void unzipFile(MultipartFile file, Path destPath) throws IOException {
    try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream(), StandardCharsets.UTF_8)) {
        ZipEntry entry;
        while ((entry = zipInputStream.getNextEntry()) != null) {
            if (!entry.isDirectory()) {
                Path filePath = destPath.resolve(entry.getName());
                Files.createDirectories(filePath.getParent());
                try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
                    IoUtil.copy(zipInputStream, fos);
                }
            }
            zipInputStream.closeEntry();
        }
    }
}
GDB 文件查找
java 复制代码
public static List<File> findGdbFiles(File directory) {
    List<File> gdbFiles = new ArrayList<>();
    File[] files = directory.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isDirectory()) {
                // 检查是否为GDB目录(.gdb扩展名的目录)
                if (file.getName().toLowerCase().endsWith(".gdb")) {
                    gdbFiles.add(file);
                } else {
                    // 递归查找子目录
                    gdbFiles.addAll(findGdbFiles(file));
                }
            } else if (isGdbFile(file.getName())) {
                gdbFiles.add(file);
            }
        }
    }
    return gdbFiles;
}

第4步:GDB 文件解析 (GbdFileParser)

多图层 GDB 解析
java 复制代码
public static MultiLayerGdbResult parseMultiLayerGdbWithGdal(File gdbFile, String dataType) {
    MultiLayerGdbResult result = new MultiLayerGdbResult();
    Map<String, List<?>> layerDataMap = new HashMap<>();
    
    try {
        // 使用 GDAL 解析 GDB 文件
        gdal.AllRegister();
        DataSource dataSource = ogr.Open(gdbFile.getAbsolutePath());
        
        if (dataSource == null) {
            throw new RuntimeException("无法打开 GDB 文件: " + gdbFile.getAbsolutePath());
        }
        
        int layerCount = dataSource.GetLayerCount();
        
        for (int i = 0; i < layerCount; i++) {
            Layer layer = dataSource.GetLayer(i);
            String layerName = layer.GetName();
            
            // 根据数据类型过滤图层
            if (isValidLayer(layerName, dataType)) {
                List<?> layerData = parseLayerData(layer, dataType);
                layerDataMap.put(layerName, layerData);
            }
        }
        
        result.setLayerDataMap(layerDataMap);
        result.setLayerNames(new ArrayList<>(layerDataMap.keySet()));
        
    } catch (Exception e) {
        log.error("解析 GDB 文件失败: {}", gdbFile.getName(), e);
        throw new RuntimeException("解析 GDB 文件失败: " + e.getMessage());
    }
    
    return result;
}

第5步:图层数据处理

图层分发处理
java 复制代码
private int processLayerData(String layerName, List<?> layerData, boolean overwriteMode, List<String> errorList) {
    int successCount = 0;
    
    try {
        switch (layerName.toLowerCase()) {
            case "hlscclysj":
                successCount = processHlData((List<DbHlscclysj>) layerData, overwriteMode, errorList);
                break;
            case "hpscclysj":
                successCount = processHpData((List<DbHpscclysj>) layerData, overwriteMode, errorList);
                break;
        }
        
    } catch (Exception e) {
        log.error("处理图层 {} 数据时发生异常: {}", layerName, e.getMessage(), e);
        errorList.add("图层 " + layerName + " 处理异常: " + e.getMessage());
    }
    
    return successCount;
}

第6步:数据库操作

水库数据处理 (processSkData)
java 复制代码
private int processSkData(List<DbSkscclysj> layerData, boolean overwriteMode, List<String> errorList) {
    int successCount = 0;
    
    for (DbSkscclysj xzq : layerData) {
        try {
            // 检查是否存在重复数据
            QueryWrapper<DbSkscclysj> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(DbSkscclysj::getSkbm, xzq.getSkbm());
            queryWrapper.lambda().eq(DbSkscclysj::getXjxzqdm, xzq.getXjxzqdm());
            queryWrapper.lambda().eq(DbSkscclysj::getSjclsj, xzq.getSjclsj());
            DbSkscclysj existingXzq = this.getOne(queryWrapper);
            
            if (existingXzq != null) {
                if (overwriteMode) {
                    // 覆盖模式:更新现有数据
                    xzq.setId(existingXzq.getId());
                    this.baseMapper.updateWithGeometry(xzq);
                    successCount++;
                } else {
                    // 跳过模式:跳过重复数据
                    errorList.add("水库代码 " + xzq.getSkbm() + " 已存在,跳过导入");
                }
            } else {
                // 新增数据,使用自定义方法处理几何数据
                xzq.setId(IdUtil.simpleUUID());
                this.baseMapper.insertWithGeometry(xzq);
                successCount++;
            }
        } catch (Exception e) {
            log.error("导入水库数据失败: {}", e.getMessage(), e);
            errorList.add("水库数据导入失败: " + e.getMessage());
        }
    }
    return successCount;
}
数据库映射
xml 复制代码
<!-- 自定义插入方法,处理几何数据 -->
<insert id="insertWithGeometry" parameterType="vip.biz.db.skscclysj.entity.DbSkscclysj">
    insert into db_skscclysj (
    id, delete_flag, create_user, create_time,update_time, update_user,
    ysdm, skbm, skmc, xjxzqmc, xjxzqdm, ssl, sslxdm, sxdxsjly, zkr, fsqsyxj, ksqsyxj,
    npjsymj, ksqccyl, fsqccyl, npjsccyl, sccljsff, ytlx, kjlx, bz, sjclr, sjclsj, jcr, jcsj, shhr, shsj,
    sxdxsj, jgdysj, sjsx, gxpl, sjnf
    ) values (
    #{id}, #{deleteFlag}, #{createUser}, #{createTime}, #{updateTime}, #{updateUser},
    #{ysdm},#{skbm}, #{skmc}, #{xjxzqmc}, #{xjxzqdm}, #{ssl}, #{sslxdm}, #{sxdxsjly}, #{zkr}, #{fsqsyxj}, #{ksqsyxj},
    #{npjsymj}, #{ksqccyl}, #{fsqccyl}, #{npjsccyl}, #{sccljsff}, #{ytlx}, #{kjlx}, #{bz}, #{sjclr}, #{sjclsj}, #{jcr}, #{jcsj}, #{shhr}, #{shsj},
    #{sxdxsj}, #{jgdysj}, #{sjsx}, #{gxpl}, #{sjnf}
    )
</insert>

<!-- 自定义更新方法 -->
<update id="updateWithGeometry" parameterType="vip.xxx.DbSkscclysj">
    UPDATE db_skscclysj
    <set>
        <!-- 更新字段 -->
    </set>
    WHERE id = #{id}
</update>

技术依赖

关键依赖库

xml 复制代码
<!-- MyBatis-Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- GeoTools(地理空间数据处理) -->
<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-main</artifactId>
    <version>25.0</version>
</dependency>

<!-- GDAL(地理空间数据库) -->
<dependency>
    <groupId>org.gdal</groupId>
    <artifactId>gdal</artifactId>
    <version>3.6.0</version>
</dependency>

<!-- Hutool(工具类) -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

<!-- EasyExcel(Excel 处理) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

GDAL 环境配置

bash 复制代码
# Windows 环境变量配置
PATH: C:\OSGeo4W64\bin
GDAL_DATA: C:\OSGeo4W64\share\gdal
PROJ_LIB: C:\OSGeo4W64\share\proj

# JVM 参数配置
-Djava.library.path=C:\OSGeo4W64\bin
-Dgdal.data=C:\OSGeo4W64\share\gdal

错误处理

常见异常类型

  1. 文件格式错误: 文件不是 ZIP 格式
  2. 未找到 GDB 文件: ZIP 文件中没有 GDB 格式文件
  3. 图层解析失败: GDAL 解析 GDB 文件失败
  4. 数据重复: 根据唯一标识字段检查重复
  5. 数据库操作失败: 数据库插入或更新失败

异常处理策略

  • 事务回滚 : 使用 @Transactional 确保数据一致性
  • 错误收集: 收集所有错误信息,不因单个错误中断
  • 日志记录: 详细记录处理过程和错误信息
  • 结果统计: 返回成功和失败的统计信息

总结

这个接口是一个复杂的多功能地理空间数据导入接口,通过分层架构实现了从文件接收到数据库存储的完整流程。该接口支持多种水体类型的导入,具有良好的错误处理和性能优化,为水体数据管理提供了强大的数据导入能力。

相关推荐
jjjava2.01 小时前
软件测试与开发全流程解析
java·功能测试·测试用例
CodeStats1 小时前
JavaWeb 造轮者视角:Spring Boot 启动核心思想与完整链路解析
java·spring boot·后端
weixin_523185321 小时前
Spring事务为什么会失效?常见场景与解决方案总结
java·数据库·spring
cfm_29141 小时前
JVM对象逃逸分析深度详解
java·开发语言·jvm
云絮.1 小时前
数据库约束
java·数据库·sql·mysql·oracle
weixin_523185321 小时前
SimpleDateFormat为什么线程不安全?源码级解析与解决方案
java·开发语言·安全
Chase_______1 小时前
【Java杂项】Java 中的 null:空指针、自动拆箱与集合边界详解
java·开发语言
程序猿乐锅1 小时前
【JAVASE | 第十九篇】Java 注解入门
java
布朗克1681 小时前
28 网络编程——Socket、TCP/UDP与HttpClient
java·网络·tcp/ip·udp