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.实现效果

相关推荐
码农水水1 小时前
国家电网Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·网络·分布式·面试·wpf
qq_336313932 小时前
java基础-网络编程-TCP
java·网络·tcp/ip
咕噜咕噜啦啦2 小时前
Java期末习题速通
java·开发语言
盐真卿3 小时前
python2
java·前端·javascript
一嘴一个橘子4 小时前
mybatis - 动态语句、批量注册mapper、分页插件
java
组合缺一4 小时前
Json Dom 怎么玩转?
java·json·dom·snack4
危险、4 小时前
一套提升 Spring Boot 项目的高并发、高可用能力的 Cursor 专用提示词
java·spring boot·提示词
kaico20184 小时前
JDK11新特性
java
钊兵4 小时前
java实现GeoJSON地理信息对经纬度点的匹配
java·开发语言
jiayong234 小时前
Tomcat性能优化面试题
java·性能优化·tomcat