资源相关

This commit is contained in:
ZZX9599
2025-09-16 11:41:45 +08:00
parent eda0bc0999
commit 89df7e6c0e
22 changed files with 1011 additions and 64 deletions

View File

@ -2,6 +2,17 @@ package com.yj.earth.business.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -9,6 +20,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yj.earth.business.domain.FileInfo;
import com.yj.earth.business.service.FileInfoService;
import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.common.util.JsonMapConverter;
import com.yj.earth.vo.FileInfoVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -17,17 +29,16 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@Tag(name = "文件数据管理")
@ -168,11 +179,7 @@ public class FileInfoController {
@Operation(summary = "文件预览")
@GetMapping("/preview/{id}")
public void previewFile(
@Parameter(description = "文件ID", required = true)
@PathVariable String id,
HttpServletResponse response) throws IOException {
public void previewFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException {
// 根据ID查询文件信息
FileInfo fileInfo = fileInfoService.getById(id);
if (fileInfo == null) {
@ -200,42 +207,100 @@ public class FileInfoController {
}
}
@Operation(summary = "文件列表")
@GetMapping("/list")
public ApiResponse getFileList(
@Parameter(description = "页码", required = true) Integer pageNum,
@Parameter(description = "每页条数", required = true) Integer pageSize,
@Parameter(description = "文件名称") String fileName) {
// 创建分页对象
Page<FileInfo> page = new Page<>(pageNum, pageSize);
// 构建查询条件
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
if (fileName != null && !fileName.isEmpty()) {
queryWrapper.like(FileInfo::getFileName, fileName);
public String handleLocationImageUpload(MultipartFile file) {
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());
// 检查文件是否已存在
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(FileInfo::getFileName, originalFilename)
.eq(FileInfo::getFileMd5, fileMd5);
if (fileInfoService.count(queryWrapper) > 0) {
throw new IllegalStateException("已存在相同的图片文件");
}
// 提取图片元数据(使用已保存的文件,避免使用临时文件)
Map<String, Object> 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);
fileInfoService.save(fileInfo);
// 构建并返回结果
Map<String, Object> result = new HashMap<>();
result.put("previewUrl", "/fileInfo/preview/" + fileInfo.getId());
result.put("downloadUrl", "/fileInfo/download/" + fileInfo.getId());
result.put("metadata", metadata);
return JsonMapConverter.mapToJson(result);
} catch (IOException e) {
throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
}
// 按创建时间倒序排列、最新上传的文件在前
queryWrapper.orderByDesc(FileInfo::getCreatedAt);
}
// 执行分页查询
IPage<FileInfo> fileInfoPage = fileInfoService.page(page, queryWrapper);
/**
* 计算文件的 MD5
*/
private String calculateFileMd5(File file) throws IOException {
try (InputStream is = new FileInputStream(file)) {
return DigestUtils.md5DigestAsHex(is);
}
}
// 转换为VO对象并设置URL
List<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> {
FileInfoVo vo = new FileInfoVo();
BeanUtils.copyProperties(fileInfo, vo);
vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId());
vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId());
return vo;
}).collect(Collectors.toList());
/**
* 提取图片的EXIF元数据包括定位信息
*/
private Map<String, Object> extractImageMetadata(InputStream inputStream) {
try {
Map<String, Object> result = new HashMap<>();
Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
// 构建分页结果
Page<FileInfoVo> resultPage = new Page<>();
resultPage.setRecords(records);
resultPage.setTotal(fileInfoPage.getTotal());
resultPage.setSize(fileInfoPage.getSize());
resultPage.setCurrent(fileInfoPage.getCurrent());
resultPage.setPages(fileInfoPage.getPages());
return ApiResponse.success(resultPage);
// 遍历所有元数据目录
for (Directory directory : metadata.getDirectories()) {
String directoryName = directory.getName();
Map<String, String> directoryTags = new HashMap<>();
// 提取当前目录下的所有标签
for (com.drew.metadata.Tag tag : directory.getTags()) {
directoryTags.put(tag.getTagName(), tag.getDescription());
}
// 存储当前目录的所有标签
if (!directoryTags.isEmpty()) {
result.put(directoryName, directoryTags);
}
}
return result;
} catch (ImageProcessingException | IOException e) {
return Collections.emptyMap();
}
}
}