模型库
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