Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot

当前任务是在SpringBoot服务中.,将kml、geojson、包含shp文件的文件夹的zip文件解析为geojson字符串

kml文件和geojson文件

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第1张图片

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第2张图片

 

 

其中zip文件结构如图

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第3张图片

点开同名文件夹后有如下矢量文件

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第4张图片

之前尝试过在window上配置gdal,但是由于需要在linux上运行,配置过程中涉及到的dll文件不跨平台,于是重新尝试使用geotool工具来解析文件 

一、导入getool依赖


        
            com.amazonaws
            aws-java-sdk
            1.5.5
        

        
            org.geotools
            gt-geojson
            24.2
        
        
            org.geotools
            gt-shapefile
            22.3
        
        
            org.geotools
            gt-main
            24.2
        
        
            org.geotools.xsd
            gt-xsd-kml
            24.2
        

二、添加工具类、实体类

字符串工具类(用于路径、文件名截取)

package com.tx.agriculture.util;


public class StringUtil {

    //获取路径最后的文件(夹)
    public static String getFileNameFromPath(String path){
        int length = path.split("\\\\").length;
        String s = path.split("\\\\")[length - 1];
        return s;
    }
    public static String getTypeFromFileName(String fileName){
        int i = fileName.lastIndexOf(".");
        String substring = fileName.substring(i);
        return substring;
    }


    public static void main(String[] args) {
        String typeFromFileName = StringUtil.getTypeFromFileName("asdasklda.zip");
        System.out.println(typeFromFileName);

        String fileNameFromPath = StringUtil.getFileNameFromPath("a\\b\\aweq\\qweq\\test\\area.mkl");
        System.out.println(fileNameFromPath);

    }
}

涉及到的实体类 

package com.tx.common.core.exception;

/**
 * 错误代码枚举
 *
 * @author c20220052
 * @date 2022/12/29
 */
public enum ErrorCodeEnum{
    SUCCESS(200,"操作成功"),
    PARAM_ERROR(201,"参数异常"),
    PARAM_NULL(203,"参数为空"),
    PARAM_FORMAT_ERROR(204,"参数格式不正确"),
    PARAM_VALUE_ERROR(205,"参数值不正确"),
    DUPLICATE_PRIMARY_KEY(206,"唯一键冲突"),
    SYSTEM_ERROR(207,"系统异常"),
    UNKNOWN_ERROR(208,"未知异常"),
    AUTH_ERROR(209,"认证失败"),
    NO_PERMISSION_ERROR(210,"没有权限"),
    INVALID_REQUEST_ERROR(211,"无效请求"),
    SERVER_ERROR(212,"第三方服务异常")
    ;
    private int code;
    private String message;

    ErrorCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}
package com.tx.common.core.vo;

import com.tx.common.core.exception.ErrorCodeEnum;

import java.io.Serializable;

/**
 * 返回结果
 *
 * @author jhj
 * @date 2022/12/29
 */
public class Result implements Serializable {
    private Integer code;
    private String msg;
    private Object data;
    public static Result success(){
        Result result = new Result();
        result.code = ErrorCodeEnum.SUCCESS.getCode();
        return result;
    }
    public static Result success(Object data){
        Result result = new Result();
        result.code = ErrorCodeEnum.SUCCESS.getCode();
        result.data = data;
        return result;
    }
    public static Result failure(Integer code, String message){
        Result result = new Result();
        result.code = code;
        result.msg = message;
        return result;
    }
    public static Result failure(ErrorCodeEnum errorCodeEnum){
        Result result = new Result();
        result.code = errorCodeEnum.getCode();
        result.msg = errorCodeEnum.getMessage();
        return result;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

 

package com.tx.agriculture.domain.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 文件 用于接收pojo文件参数
 *
 * @author jhj
 * @date 2022/12/22
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class MyFile {
    private String name;
    private String absulotePath;
}

文件解析工具类

import com.amazonaws.util.json.JSONArray;
import com.amazonaws.util.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
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.geotools.geojson.feature.FeatureJSON;
import org.geotools.kml.KMLConfiguration;
import org.geotools.xsd.PullParser;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import java.util.Enumeration;
import java.util.zip.ZipFile;

@Slf4j
public class FileParse {
    //这里添入文章提到的函数

    //判断完整性

    //zip文件解压

    //geojson文件解析

    //kml文件解析

    //shp文件解析

}

 

在这里先不忙写代码,先整理一下大概思路。

根据需求我们需要解压、分析方法

由于涉及一些文件操作,跨操作系统会涉及到路径问题,这里我采用的是将路径写在yml配置文件中(也可以运行时获取绝对路径),总之要保证路径是正确的

此外压缩文件中的shp需要其他辅助文件(prj)才能正常读取,因此需要进行文件完整性的判定。

然后就是解析,根据目前三种文件,设计三个方法来解析,另外增加文件解压、文件删除的方法函数

yml添加如下路径配置(在该路径下的文件夹中进行解压、创建和删除)

base-upload-path: C:/www/changzhou/static/res

1.判断文件是否完整 函数

/**
     * 是完整
     *
     * @param zipInputStream zip编码输入流
     * @return {@link Map}<{@link String},{@link Object}>
     * @throws IOException ioexception
     */
    public static Map isComplete(ZipInputStream zipInputStream) throws IOException {
        Map map = new HashMap<>();
        ZipInputStream zin =zipInputStream;
        //wheather there are shp/prj files
        Boolean flag1= false;
        Boolean flag2= false;

        //遍历整个文件结构
        ZipEntry ze;
        String dirName="";
        while((ze = zin.getNextEntry()) != null){
            if(dirName.equals("")){
                dirName=ze.getName();
            }
            if(ze.toString().endsWith("shp")){
                flag1=true;
//                log.info("shape file!");
            }
            else if(ze.toString().endsWith("prj")) {
                flag2=true;
//                log.info("prj file!");
            }
        }
        zin.closeEntry();

        map.put("dirName",dirName.split("/")[0]);

        //如果必要文件存在缺失,返回false
        if(!(flag1&&flag2)){
            map.put("complete",false);
            return map;
//            throw new IOException();
        }
        map.put("complete",true);
        return map;
    }

2.解压zip文件函数

/**
     * 解压zip文件
     *
     * @param srcPath zip文件路径
     * @param desPath 目标路径
     */
    public static void decompression(String srcPath,String desPath){
        //20MB
        int BUFFER = 1024*1024*20;
        String fileName = "";

        try {
            //BOS BIS ZE ZF
            BufferedOutputStream dest = null;
            BufferedInputStream is = null;
            ZipEntry entry;
            ZipFile zipfile = new ZipFile(srcPath);

            //遍历
            Enumeration dir = zipfile.entries();
            while (dir.hasMoreElements()){
                entry = (ZipEntry) dir.nextElement();
                //
                if( entry.isDirectory()){
                    fileName = entry.getName();
                    fileName = fileName.substring(0, fileName.length() - 1);
                    File fileObject = new File(desPath +"\\"+ fileName);
                    //创建文件夹
                    if(!fileObject.exists()){
                        fileObject.mkdir();
                    }
                }
            }

            Enumeration e = zipfile.entries();
            //解压
            while (e.hasMoreElements()) {
                entry = (ZipEntry) e.nextElement();
                if( entry.isDirectory()){
                    continue;
                }else{
                    is = new BufferedInputStream(zipfile.getInputStream(entry));
                    int count;
                    byte[] dataByte = new byte[BUFFER];
                    FileOutputStream fos = new FileOutputStream(desPath+entry.getName());
                    dest = new BufferedOutputStream(fos, BUFFER);
                    while ((count = is.read(dataByte, 0, BUFFER)) != -1) {
                        dest.write(dataByte, 0, count);
                    }
                    dest.flush();
                    dest.close();
                    is.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

3.geojson文件解析函数

    /**
     * geojson文件 解析为 geojson
     *
     * @param path 路径
     * @throws IOException ioexception
     */
    public static String resolutionGeojson(String path) throws IOException  {
        File file = new File(path);
        FileReader fileReader = new FileReader(file);
        Reader reader = new InputStreamReader(new FileInputStream(file), "Utf-8");
        int ch = 0;
        StringBuffer sb = new StringBuffer();
        while ((ch = reader.read()) != -1) {
            sb.append((char) ch);
        }
        fileReader.close();
        reader.close();
        String jsonStr = sb.toString();
//        System.out.printf(jsonStr);
//        System.out.println(JSON.parseObject(jsonStr));
        return jsonStr;
    }

4.kml文件解析函数

/**
     * kml转换为geojson
     *
     * @param input 输入
     * @return {@link StringWriter}
     * @throws IOException ioexception
     */
    public static String kml2Geojson(InputStream input) throws IOException {
        try {
            PullParser parser = new PullParser(new KMLConfiguration(), input, SimpleFeature.class);
            FeatureJSON fjson = new FeatureJSON();
            StringWriter sw = new StringWriter();
            SimpleFeature simpleFeature = (SimpleFeature) parser.parse();
            sw.append("{    \"type\": \"FeatureCollection\",    \"features\": [");
            while (simpleFeature != null) {
                SimpleFeatureType featureType = simpleFeature.getFeatureType();
                fjson.setFeatureType(featureType);
                fjson.writeFeature(simpleFeature, sw);
                simpleFeature = (SimpleFeature) parser.parse();
                if (simpleFeature != null) {
                    sw.append(",");
                }
            }
            sw.append("]}");
            sw.close();
            return sw.toString();
        } catch (Exception e) {
            log.error("KML 文件解析报错:{}", e.getMessage());
            return null;
        } finally {
            Objects.requireNonNull(input,"KML 文件输入流为空").close();
        }
    }

5.shp文件解析函数

    /**
     * shp转换为Geojson
     * @param shpPath
     * @return
     */
    public static Map shape2Geojson(String shpPath){
        Map map = new HashMap();
        FeatureJSON fjson = new FeatureJSON();
        String dir = shpPath.split("\\\\")[shpPath.split("\\\\").length-1];
        shpPath = shpPath+"\\"+dir+".shp";

        try{
            StringBuffer sb = new StringBuffer();
            //json
            sb.append("{\"type\": \"FeatureCollection\",\"features\": ");

            File file = new File(shpPath);
            ShapefileDataStore shpDataStore = new ShapefileDataStore(file.toURL());
            //设置编码
            Charset charset = Charset.forName("GBK");
            shpDataStore.setCharset(charset);
            //
            String typeName = shpDataStore.getTypeNames()[0];
            SimpleFeatureSource featureSource = shpDataStore.getFeatureSource (typeName);
            //
            SimpleFeatureCollection result = featureSource.getFeatures();
            SimpleFeatureIterator itertor = result.features();

            //使用array存储json JSONObject
            JSONArray array = new JSONArray();
            while (itertor.hasNext())
            {
                SimpleFeature feature = itertor.next();
                StringWriter writer = new StringWriter();
                fjson.writeFeature(feature, writer);
                com.amazonaws.util.json.JSONObject json = new JSONObject(writer.toString());
                array.put(json);
            }
            itertor.close();
            //写入StringBuffer
            sb.append(array.toString());
            sb.append("}");

//            log.info(String.valueOf(sb));
            //写入文件
//            cm.append2File(jsonPath, sb.toString());

            map.put("status", "success");
            map.put("message", sb.toString());
        }
        catch(Exception e){
            map.put("status", "failure");
            map.put("message", e.getMessage());
            e.printStackTrace();
        }
        return map;
    }

6.文件删除

    /**
     * 删除dir !!谨慎操作!!
     */
    public static void flushDir(String desPath) {
        Path path = Paths.get(desPath);
        try {
            Files.walkFileTree(path, new SimpleFileVisitor() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
                    Files.delete(file);
//                    log.info("删除文件: {}", file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir,
                                                          IOException exc) throws IOException {
                    Files.delete(dir);
                    log.info("文件夹被删除: {}", dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void deleteFile(String path){
        File file = new File(path);
        file.delete();
    }

至此完成了工具类的编写

三、创建controller

package com.tx.agriculture.controller;


import cn.hutool.core.io.file.FileNameUtil;
import com.alibaba.fastjson.JSONObject;
import com.tx.agriculture.domain.entity.MyFile;
import com.tx.agriculture.service.UploadService;
import com.tx.common.core.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
@RequestMapping("/api/upload")
public class UploadController {
    @Value("${base-upload-path}")
    private String baseUploadPath;
    @PostMapping("/file")
    public Result file(MultipartFile file) throws IOException {
        Path basePath = Paths.get(baseUploadPath);
        String filename = file.getOriginalFilename();
        String prefix = FileNameUtil.getPrefix(filename);
        String suffix = FileNameUtil.getSuffix(filename);
        int i = 1;
        StringBuilder sb = new StringBuilder();
        Path path = basePath.resolve(filename);
        while (Files.exists(path)){
            filename = sb.append(prefix).append(i++).append(".").append(suffix).toString();
            path = basePath.resolve(filename);
            sb.setLength(0);
        }
        file.transferTo(path);
        return Result.success(path.toUri().toString().substring(basePath.getParent().toUri().toString().length()-1));
    }
    @Autowired
    UploadService uploadService;
    /**
     * 上传文件
     *
     * @param file 文件
     * @return {@link Result}
     * @throws IOException ioexception
     */
    @PostMapping("/parse")
    public Result parse(MultipartFile file) throws IOException {

        Path basePath = Paths.get(baseUploadPath);
        String filename = file.getOriginalFilename();
        String prefix = FileNameUtil.getPrefix(filename);
        String suffix = FileNameUtil.getSuffix(filename);
        JSONObject jsonObject = new JSONObject();

        StringBuilder sb = new StringBuilder();
        Path path = basePath.resolve(filename);
        //多文件上传
        int i = 1;
        while (Files.exists(path)){
            filename = sb.append(prefix).append(i++).append(".").append(suffix).toString();
            path = basePath.resolve(filename);
            sb.setLength(0);
        }

        //存储到本地
        file.transferTo(path);

        //对文件进行解析
        try{
            jsonObject = uploadService.fileUpload(file, path);
        }catch (Exception e){
            //文件参数异常
            return Result.failure(201,e.getMessage());
        }

        return Result.success(jsonObject);
    }

    /**
     * 删除文件(夹)
     *
     * @param myFile 包含文件名
     * @return {@link Result}
     */
    @PostMapping("/delete")
    public Result delete(@RequestBody MyFile myFile){
        String name =myFile.getName();
        String path =baseUploadPath+"\\"+name;
        try{
            uploadService.fileDelete(path);
        } catch(Exception e){
            return Result.failure(205,"当前文件不存在:"+name);
        }
        return Result.success("当前文件成功删除:"+name);
    }

}

四、service接口层及其实现类

package com.tx.agriculture.service;

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Path;
public interface UploadService {
    public JSONObject fileUpload(MultipartFile multipartFile, Path path) throws Exception;
    public void fileDelete(String name) throws Exception;
}
package com.tx.agriculture.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tx.agriculture.service.UploadService;
import com.tx.agriculture.util.FileParse;
import com.tx.agriculture.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.zip.ZipInputStream;
import java.nio.file.Path;
@Slf4j
@Service
public class UploadServiceImpl  implements UploadService {


    /**
     * 文件上传
     *
     * @param multipartFile 多媒体文件
     * @param path          目录+文件 路径
     */
    @Override
    public JSONObject fileUpload(MultipartFile multipartFile, Path path) throws Exception{
        //最终存入数据库的字符串 geojson
        String geojson="";
        String name = multipartFile.getName();
        //分割路径
        String sep = File.separator;
        String filePath=path.toString();

        //需要返回当前文件名
        int length = filePath.split("\\\\").length;
        String actualFileName=filePath.split("\\\\")[length-1];

        //multipartFile 先转为 inputStream
        InputStream inputStream=multipartFile.getInputStream();


        log.info("正在上传文件:"+name);
        //判断 文件是否是压缩包
        //文件类型
        String type =filePath.substring(filePath.length()-3);
        if(type.equals("zip")){
            //判断文件完整性:inputStream 转为 ZipInputStream
            Charset gbk = Charset.forName("gbk");
            ZipInputStream zin = new ZipInputStream(inputStream,gbk);
            Boolean complete = null;

            Map resultMap=FileParse.isComplete(zin);
            complete = (Boolean) resultMap.get("complete");
            String dirName= (String) resultMap.get("dirName");
            if(complete){
                log.info("正在解压文件");
                //解压文件
                String srcPath=path.toString();
                int len = srcPath.split("\\\\").length;
                String zipName=srcPath.split("\\\\")[len-1];

                String desPath= srcPath.substring(0,srcPath.length()-zipName.length());
                FileParse.decompression(srcPath,desPath);

                //解析文件为geojson
                try{
                    //参数是zip文件 解压后的 文件路径

                    String dirPath =filePath.substring(0,filePath.length()-zipName.length())  + dirName;
                    geojson = (String) FileParse.shape2Geojson(dirPath).get("message");
                }catch (Exception e){
                    e.printStackTrace();
                }

            }else {
                log.info("necessary files don't exist :必要文件存在缺失...");
                throw new IllegalArgumentException();
            }

        }
        else if(type.equals("kml")){

            geojson = FileParse.kml2Geojson(inputStream);

        }else if (type.equals("son")){
            //geojson文件
            try{
                geojson = FileParse.resolutionGeojson(path.toString());
            }catch (Exception e){
                e.printStackTrace();
                log.info("geojson files are not formatted 文件格式错误:geojson...");
            }
        }else {
            log.info("files are not supported 暂时不支持当前文件类型上传...");
            throw new IllegalArgumentException();
        }

        log.info(geojson);
        JSONObject jsonObject = (JSONObject) JSONObject.parse(geojson);
        return jsonObject;
    }


    //删除文件
    @Override
    public void fileDelete(String  path) throws Exception {
        String fileName = StringUtil.getFileNameFromPath(path);
        //删除服务器临时文件
        File file = new File(path);
        //文件不存在
        if(!file.exists()){
            throw new Exception();
        }
        if(file.isFile()){
            FileParse.deleteFile(path);
        }
        else{
            FileParse.flushDir(path);
        }
        log.info("正在删除文件:"+ fileName+"...");
    }
}

注意,其中的文件路径字符串截取方式可能有些过于简单粗暴,不一定适合所有场景。有待改良

启动springboot服务后,我们进行一个简单的测试

五、测试文件上传

选择上传文件

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第5张图片

zip文件返回结果Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第6张图片

kml文件返回结果

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第7张图片

geojson文件返回结果

Java代码实现kml文件、geojson文件、shp矢量文件的解析#java实现zip文件解压#geotool#SpringBoot_第8张图片

 这样就完成了基本的解析功能

你可能感兴趣的:(springboot开发基础,java)