Spring Boot 上传shp压缩包解析多少地块

1.依赖(pom.xml

XML 复制代码
 <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-main</artifactId>
            <version>29.0</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-shapefile</artifactId>
            <version>29.0</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>29.0</version>
        </dependency>

2.代码实现

java 复制代码
package com.example.demo.controller;

import org.apache.tomcat.jni.FileInfo;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@RestController
@RequestMapping("/file")
public class FileController {
 
    // 在类中定义一个基础临时目录(相对于项目根目录)
    private static final String BASE_TEMP_DIR = "temp";

    @PostMapping("/uploadshp")
    public ResponseEntity<Map<String, Object>> uploadSHP(@RequestParam("file") MultipartFile file) {
        Map<String, Object> result = new HashMap<>();
        Path tempDir = null;

        try {
            // 1. 创建项目下的临时目录: ./temp/shp-upload-xxxxx
            Path baseTempPath = Paths.get(BASE_TEMP_DIR).toAbsolutePath().normalize();
            Files.createDirectories(baseTempPath); // 确保 temp 目录存在
            tempDir = Files.createTempDirectory(baseTempPath, "shp-upload-");
            unzipFile(file, tempDir.toAbsolutePath().toString());

            String shpFilePath = findShpFile(tempDir.toAbsolutePath().toFile());
            if (shpFilePath == null) {
                throw new RuntimeException("压缩包中未找到 .shp 文件");
            }

            Map<String, Object> analysis = analyzeShpFeatures(shpFilePath);
            String geometryType = (String) analysis.get("geometryType");
            int count = (int) analysis.get("featureCount");

            String message;
            switch (geometryType) {
                case "Point":
                    message = "成功解析,共 " + count + " 个点";
                    break;
                case "Polygon":
                    message = "成功解析,共 " + count + " 个地块";
                    break;
                case "LineString":
                    message = "成功解析,共 " + count + " 条线(非点/面)";
                    break;
                case "Empty":
                    message = "SHP 文件为空,无任何要素";
                    break;
                default:
                    message = "不支持的几何类型: " + geometryType + ",共 " + count + " 个要素";
                    break;
            }

            result.put("success", true);
            result.put("geometryType", geometryType);
            result.put("featureCount", count);
            result.put("message", message);

        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "解析失败: " + e.getMessage());
            e.printStackTrace();
        } finally {
            if (tempDir != null) {
                try {
                    deleteRecursively(tempDir);
                } catch (IOException ignored) {
                }
            }
        }

        return ResponseEntity.ok(result);
    }

    // 解压 ZIP 文件(带路径遍历防护)
    private void unzipFile(MultipartFile zipFile, String destDir) throws IOException {
        try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                // 规范化路径,防止 ../ 攻击
                Path destPath = Paths.get(destDir).normalize();
                Path filePath = destPath.resolve(entry.getName()).normalize();

                if (!filePath.startsWith(destPath)) {
                    throw new IOException("非法文件路径,可能包含路径遍历攻击: " + entry.getName());
                }

                if (entry.isDirectory()) {
                    Files.createDirectories(filePath);
                } else {
                    Files.createDirectories(filePath.getParent());
                    Files.copy(zis, filePath, StandardCopyOption.REPLACE_EXISTING);
                }
                zis.closeEntry();
            }
        }
    }

    // 查找 .shp 文件(忽略大小写)
    private String findShpFile(File dir) {
        File[] files = dir.listFiles();
        if (files == null) {
            return null;
        }

        for (File file : files) {  // ✅ 遍历 File 对象
            if (file.isFile()) {
                String name = file.getName();
                if (name.toLowerCase().endsWith(".shp")) {
                    return file.getAbsolutePath();
                }
            } else if (file.isDirectory()) {
                String result = findShpFile(file); // 递归子目录
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    // 分析 SHP 文件:返回几何类型和要素总数
    private Map<String, Object> analyzeShpFeatures(String shpFilePath) throws IOException {
        File file = new File(shpFilePath);
        ShapefileDataStore dataStore = new ShapefileDataStore(file.toURI().toURL());
        dataStore.setCharset(Charset.forName("UTF-8")); // 支持中文属性

        try {
            SimpleFeatureSource source = dataStore.getFeatureSource();
            SimpleFeatureCollection features = source.getFeatures();

            Map<String, Object> result = new HashMap<>();
            if (features.isEmpty()) {
                result.put("geometryType", "Empty");
                result.put("featureCount", 0);
                return result;
            }

            // 获取第一个要素判断类型(SHP 所有要素类型一致)
            String geomTypeName = "Unknown";
            try (SimpleFeatureIterator it = features.features()) {
                if (it.hasNext()) {
                    var feature = it.next();
                    Object geomObj = feature.getDefaultGeometry();
                    if (geomObj instanceof Geometry geom) {
                        geomTypeName = geom.getGeometryType(); // 如 "MultiPoint", "Polygon"
                    }
                }
            }

            // 标准化类型名称
            String standardizedType;
            if (geomTypeName.contains("Point")) {
                standardizedType = "Point";
            } else if (geomTypeName.contains("Polygon")) {
                standardizedType = "Polygon";
            } else if (geomTypeName.contains("Line")) {
                standardizedType = "LineString";
            } else {
                standardizedType = geomTypeName;
            }

            result.put("geometryType", standardizedType);
            result.put("featureCount", features.size()); // 所有要素都是同类型
            return result;

        } finally {
            dataStore.dispose(); // 释放文件句柄
        }
    }

    // 递归删除临时目录
    private void deleteRecursively(Path path) throws IOException {
        if (Files.exists(path)) {
            Files.walk(path)
                    .sorted((a, b) -> -a.compareTo(b)) // 先删子项,再删目录
                    .forEach(p -> {
                        try {
                            Files.delete(p);
                        } catch (IOException ignored) {
                        }
                    });
        }
    }

}

3.实现效果

相关推荐
曹牧2 小时前
Java:Math.abs()‌
java·开发语言·算法
期待のcode2 小时前
Java的泛型
java·开发语言
FPGAI2 小时前
Java学习之计算机存储规则、数据类型、标识符、键盘录入、IDEA
java·学习
AC赳赳老秦2 小时前
pbootcms模板后台版权如何修改
java·开发语言·spring boot·postgresql·测试用例·pbootcms·建站
止水编程 water_proof2 小时前
SpringBoot快速上手
java·spring boot·后端
皮卡丘学了没2 小时前
Java基础-HashMap扩容机制(Java8源码)
java·哈希算法·散列表
li.wz2 小时前
ShardingSphere 与 PolarDB-X 选型对比
java·后端·微服务
是梦终空2 小时前
计算机毕业设计260—基于Springboot+Vue3+Ai对话的非遗传承管理系统(源代码+数据库+2万字论文)
spring boot·vue·毕业设计·课程设计·毕业论文·ai对话·非遗传承管理系统
wanghowie2 小时前
02.02.02 CompletableFuture 组合与异常处理:构建复杂异步流
java·future·并发编程