资源相关
This commit is contained in:
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user