模型库
This commit is contained in:
		| @ -2,8 +2,16 @@ package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.FileInfo; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| public interface FileInfoService extends IService<FileInfo> { | ||||
|     // 根据文件ID获取文件绝对路径 | ||||
|     String getFileAbsolutePath(String id); | ||||
|     Map<String, Object> handleLocationImageUpload(MultipartFile file); | ||||
|     String uploadWithPreview(MultipartFile file); | ||||
|     void writeResponseMessage(HttpServletResponse response, String message); | ||||
|     String getFullUploadPath(); | ||||
| } | ||||
|  | ||||
| @ -1,16 +0,0 @@ | ||||
| package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| public interface ModelService extends IService<Model> { | ||||
|  | ||||
| } | ||||
| @ -1,16 +0,0 @@ | ||||
| package com.yj.earth.business.service; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import com.baomidou.mybatisplus.extension.service.IService; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| public interface ModelTypeService extends IService<ModelType> { | ||||
|  | ||||
| } | ||||
| @ -1,21 +1,38 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import cn.hutool.core.io.FileUtil; | ||||
| import com.drew.imaging.ImageMetadataReader; | ||||
| import com.drew.lang.Rational; | ||||
| import com.drew.metadata.Metadata; | ||||
| import com.drew.metadata.exif.GpsDirectory; | ||||
| import com.yj.earth.business.domain.FileInfo; | ||||
| import com.yj.earth.business.mapper.FileInfoMapper; | ||||
| import com.yj.earth.business.service.FileInfoService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import com.yj.earth.datasource.DatabaseManager; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.util.DigestUtils; | ||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
|  | ||||
| @Service | ||||
| public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService { | ||||
|  | ||||
|     @Value("${file.upload.path}") | ||||
|     private String uploadPath; | ||||
|     private static final String DEFAULT_UPLOAD_PATH = "upload"; | ||||
|  | ||||
|     public String getFileAbsolutePath(String id) { | ||||
|  | ||||
| @ -26,7 +43,7 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i | ||||
|         } | ||||
|  | ||||
|         // 构建完整文件路径 | ||||
|         String fullPath = uploadPath + File.separator + fileInfo.getFilePath(); | ||||
|         String fullPath = DEFAULT_UPLOAD_PATH + File.separator + fileInfo.getFilePath(); | ||||
|         File file = new File(fullPath); | ||||
|  | ||||
|         // 校验文件是否存在 | ||||
| @ -37,4 +54,187 @@ public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> i | ||||
|         // 获取并返回绝对路径 | ||||
|         return file.getAbsolutePath(); | ||||
|     } | ||||
|  | ||||
|     public Map<String, Object> handleLocationImageUpload(MultipartFile file) { | ||||
|         // 构建并返回结果 | ||||
|         Map<String, Object> result = new HashMap<>(); | ||||
|         try { | ||||
|             // 校验文件是否为空 | ||||
|             if (file.isEmpty()) { | ||||
|                 throw new IllegalArgumentException("上传文件不能为空"); | ||||
|             } | ||||
|             // 获取文件基本信息 | ||||
|             String originalFilename = file.getOriginalFilename(); | ||||
|             String fileSuffix = FileUtil.extName(originalFilename); | ||||
|             String contentType = file.getContentType(); | ||||
|             // 验证是否为图片文件 | ||||
|             if (contentType == null || !contentType.startsWith("image/")) { | ||||
|                 throw new IllegalArgumentException("请上传图片文件"); | ||||
|             } | ||||
|             // 获取完整的上传目录路径 | ||||
|             Path fullUploadPath = Paths.get(getFullUploadPath()); | ||||
|             // 生成唯一文件名 | ||||
|             String uniqueFileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileSuffix; | ||||
|             // 创建文件存储目录 | ||||
|             Files.createDirectories(fullUploadPath); | ||||
|             // 构建完整文件路径并保存文件 | ||||
|             Path destFilePath = fullUploadPath.resolve(uniqueFileName); | ||||
|             // 先将文件保存到目标位置 | ||||
|             file.transferTo(destFilePath); | ||||
|             // 计算文件MD5(使用已保存的文件) | ||||
|             String fileMd5 = calculateFileMd5(destFilePath.toFile()); | ||||
|             // 提取图片元数据(使用已保存的文件、避免使用临时文件) | ||||
|             Map<String, Double> metadata; | ||||
|             try (InputStream is = Files.newInputStream(destFilePath)) { | ||||
|                 metadata = extractImageMetadata(is); | ||||
|             } | ||||
|             // 保存文件信息到数据库 | ||||
|             FileInfo fileInfo = new FileInfo(); | ||||
|             fileInfo.setFileName(originalFilename); | ||||
|             fileInfo.setFileSuffix(fileSuffix); | ||||
|             fileInfo.setContentType(contentType); | ||||
|             fileInfo.setFileSize(file.getSize()); | ||||
|             fileInfo.setFilePath(uniqueFileName); | ||||
|             fileInfo.setFileMd5(fileMd5); | ||||
|             this.save(fileInfo); | ||||
|             result.put("url", "/fileInfo/preview/" + fileInfo.getId()); | ||||
|             result.put("lon", metadata.get("lon")); | ||||
|             result.put("lat", metadata.get("lat")); | ||||
|             result.put("alt", metadata.get("alt")); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("文件上传失败: " + e.getMessage(), e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public String uploadWithPreview(MultipartFile file) { | ||||
|         FileInfo fileInfo = new FileInfo(); | ||||
|         // 构建并返回结果 | ||||
|         Map<String, Object> result = new HashMap<>(); | ||||
|         try { | ||||
|             // 校验文件是否为空 | ||||
|             if (file.isEmpty()) { | ||||
|                 throw new IllegalArgumentException("上传文件不能为空"); | ||||
|             } | ||||
|             // 获取文件基本信息 | ||||
|             String originalFilename = file.getOriginalFilename(); | ||||
|             String fileSuffix = FileUtil.extName(originalFilename); | ||||
|             String contentType = file.getContentType(); | ||||
|             // 获取完整的上传目录路径 | ||||
|             Path fullUploadPath = Paths.get(getFullUploadPath()); | ||||
|             // 生成唯一文件名 | ||||
|             String uniqueFileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileSuffix; | ||||
|             // 创建文件存储目录 | ||||
|             Files.createDirectories(fullUploadPath); | ||||
|             // 构建完整文件路径并保存文件 | ||||
|             Path destFilePath = fullUploadPath.resolve(uniqueFileName); | ||||
|             // 先将文件保存到目标位置 | ||||
|             file.transferTo(destFilePath); | ||||
|             // 计算文件MD5(使用已保存的文件) | ||||
|             String fileMd5 = calculateFileMd5(destFilePath.toFile()); | ||||
|             // 保存文件信息到数据库 | ||||
|             fileInfo.setFileName(originalFilename); | ||||
|             fileInfo.setFileSuffix(fileSuffix); | ||||
|             fileInfo.setContentType(contentType); | ||||
|             fileInfo.setFileSize(file.getSize()); | ||||
|             fileInfo.setFilePath(uniqueFileName); | ||||
|             fileInfo.setFileMd5(fileMd5); | ||||
|             this.save(fileInfo); | ||||
|         } catch (IOException e) { | ||||
|             throw new RuntimeException("文件上传失败: " + e.getMessage(), e); | ||||
|         } | ||||
|         return "/fileInfo/preview/" + fileInfo.getId(); | ||||
|     } | ||||
|  | ||||
|     public void writeResponseMessage(HttpServletResponse response, String message) { | ||||
|         try { | ||||
|             response.setContentType("text/plain; charset=UTF-8"); | ||||
|             response.getWriter().write(message); | ||||
|             response.getWriter().flush(); | ||||
|         } catch (IOException e) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public String getFullUploadPath() { | ||||
|         // 拼接项目根目录和配置的上传路径 | ||||
|         return DatabaseManager.getRecommendedCacheDirectory() + File.separator + DEFAULT_UPLOAD_PATH; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 提取图片的EXIF元数据 | ||||
|      */ | ||||
|     public Map<String, Double> extractImageMetadata(InputStream inputStream) { | ||||
|         Map<String, Double> result = new HashMap<>(3); | ||||
|         try { | ||||
|             Metadata metadata = ImageMetadataReader.readMetadata(inputStream); | ||||
|             // 获取GPS相关元数据目录(图片的经纬度和高度通常存储在GPS目录中) | ||||
|             GpsDirectory gpsDirectory = metadata.getFirstDirectoryOfType(GpsDirectory.class); | ||||
|             if (gpsDirectory == null) { | ||||
|                 return result; | ||||
|             } | ||||
|  | ||||
|             // 解析纬度(度分秒转十进制) | ||||
|             Double latitude = parseLatitudeOrLongitude( | ||||
|                     gpsDirectory.getRationalArray(GpsDirectory.TAG_LATITUDE), | ||||
|                     gpsDirectory.getString(GpsDirectory.TAG_LATITUDE_REF) | ||||
|             ); | ||||
|  | ||||
|             // 解析经度(度分秒转十进制) | ||||
|             Double longitude = parseLatitudeOrLongitude( | ||||
|                     gpsDirectory.getRationalArray(GpsDirectory.TAG_LONGITUDE), | ||||
|                     gpsDirectory.getString(GpsDirectory.TAG_LONGITUDE_REF) | ||||
|             ); | ||||
|  | ||||
|             // 解析高度(考虑海拔参考、0表示海平面以上、1表示以下) | ||||
|             Double altitude = null; | ||||
|             if (gpsDirectory.containsTag(GpsDirectory.TAG_ALTITUDE)) { | ||||
|                 Rational altitudeRational = gpsDirectory.getRational(GpsDirectory.TAG_ALTITUDE); | ||||
|                 if (altitudeRational != null) { | ||||
|                     altitude = altitudeRational.doubleValue(); | ||||
|                     // 处理海拔参考(是否在海平面以下) | ||||
|                     if (gpsDirectory.containsTag(GpsDirectory.TAG_ALTITUDE_REF) && | ||||
|                             gpsDirectory.getInt(GpsDirectory.TAG_ALTITUDE_REF) == 1) { | ||||
|                         altitude = -altitude; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // 存入结果 Map | ||||
|             result.put("lat", latitude); | ||||
|             result.put("lon", longitude); | ||||
|             result.put("alt", altitude); | ||||
|         } catch (Exception e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将GPS的度分秒格式转换为十进制坐标 | ||||
|      */ | ||||
|     public Double parseLatitudeOrLongitude(Rational[] degreesMinutesSeconds, String ref) { | ||||
|         if (degreesMinutesSeconds == null || degreesMinutesSeconds.length != 3 || ref == null) { | ||||
|             return null; | ||||
|         } | ||||
|         // 度分秒转十进制:度 + 分/60 + 秒/3600 | ||||
|         double degrees = degreesMinutesSeconds[0].doubleValue(); | ||||
|         double minutes = degreesMinutesSeconds[1].doubleValue(); | ||||
|         double seconds = degreesMinutesSeconds[2].doubleValue(); | ||||
|         double value = degrees + (minutes / 60) + (seconds / 3600); | ||||
|         // 根据方向参考调整正负(南纬/西经为负) | ||||
|         if (ref.equalsIgnoreCase("S") || ref.equalsIgnoreCase("W")) { | ||||
|             value = -value; | ||||
|         } | ||||
|  | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 计算文件的 MD5 | ||||
|      */ | ||||
|     public String calculateFileMd5(File file) throws IOException { | ||||
|         try (InputStream is = new FileInputStream(file)) { | ||||
|             return DigestUtils.md5DigestAsHex(is); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import com.yj.earth.business.domain.Model; | ||||
| import com.yj.earth.business.mapper.ModelMapper; | ||||
| import com.yj.earth.business.service.ModelService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务实现类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Service | ||||
| public class ModelServiceImpl extends ServiceImpl<ModelMapper, Model> implements ModelService { | ||||
|  | ||||
| } | ||||
| @ -1,20 +0,0 @@ | ||||
| package com.yj.earth.business.service.impl; | ||||
|  | ||||
| import com.yj.earth.business.domain.ModelType; | ||||
| import com.yj.earth.business.mapper.ModelTypeMapper; | ||||
| import com.yj.earth.business.service.ModelTypeService; | ||||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * <p> | ||||
|  *  服务实现类 | ||||
|  * </p> | ||||
|  * | ||||
|  * @author 周志雄 | ||||
|  * @since 2025-09-16 | ||||
|  */ | ||||
| @Service | ||||
| public class ModelTypeServiceImpl extends ServiceImpl<ModelTypeMapper, ModelType> implements ModelTypeService { | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 ZZX9599
					ZZX9599