feat: 增强文件上传功能支持多种存储服务
- 在系统模块中集成OSS存储服务支持 - 优化FileController支持本地存储、MinIO和阿里云OSS - 增强SysFileController和SysPictureController的文件删除功能 - 支持存储服务降级机制确保上传可用性 - 优化文件访问路径生成逻辑 - 完善错误处理和日志记录机制
This commit is contained in:
parent
77d2ad543b
commit
33b341e9e7
@ -45,6 +45,12 @@
|
|||||||
<groupId>org.springdoc</groupId>
|
<groupId>org.springdoc</groupId>
|
||||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- OSS对象存储模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.leocoder.thin</groupId>
|
||||||
|
<artifactId>coder-common-thin-oss</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@ -3,22 +3,24 @@ package org.leocoder.thin.system.controller.file;
|
|||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import com.alibaba.excel.util.StringUtils;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.leocoder.thin.common.constants.CoderConstants;
|
import org.leocoder.thin.common.constants.CoderConstants;
|
||||||
import org.leocoder.thin.common.exception.BusinessException;
|
import org.leocoder.thin.common.exception.BusinessException;
|
||||||
import org.leocoder.thin.common.satoken.CoderLoginUtil;
|
import org.leocoder.thin.common.satoken.CoderLoginUtil;
|
||||||
import org.leocoder.thin.common.utils.file.FileTypeUtil;
|
import org.leocoder.thin.common.utils.file.FileTypeUtil;
|
||||||
import org.leocoder.thin.common.utils.file.UploadUtil;
|
|
||||||
import org.leocoder.thin.common.utils.ip.IpUtil;
|
import org.leocoder.thin.common.utils.ip.IpUtil;
|
||||||
import org.leocoder.thin.common.utils.ip.ServletUtil;
|
import org.leocoder.thin.common.utils.ip.ServletUtil;
|
||||||
import org.leocoder.thin.domain.pojo.system.SysFile;
|
import org.leocoder.thin.domain.pojo.system.SysFile;
|
||||||
import org.leocoder.thin.domain.pojo.system.SysPicture;
|
import org.leocoder.thin.domain.pojo.system.SysPicture;
|
||||||
|
import org.leocoder.thin.oss.service.StorageService;
|
||||||
|
import org.leocoder.thin.oss.service.StorageServiceFactory;
|
||||||
|
import org.leocoder.thin.oss.utils.OssUtil;
|
||||||
import org.leocoder.thin.system.service.file.SysFileService;
|
import org.leocoder.thin.system.service.file.SysFileService;
|
||||||
import org.leocoder.thin.system.service.picture.SysPictureService;
|
import org.leocoder.thin.system.service.picture.SysPictureService;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@ -41,6 +43,9 @@ public class FileController {
|
|||||||
@Value("${coder.filePath}")
|
@Value("${coder.filePath}")
|
||||||
private String basePath;
|
private String basePath;
|
||||||
|
|
||||||
|
@Value("${coder.storage.type:local}")
|
||||||
|
private String storageType;
|
||||||
|
|
||||||
// 允许的图片文件扩展名
|
// 允许的图片文件扩展名
|
||||||
private static final List<String> ALLOWED_IMAGE_EXTENSIONS = Arrays.asList(
|
private static final List<String> ALLOWED_IMAGE_EXTENSIONS = Arrays.asList(
|
||||||
"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"
|
"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"
|
||||||
@ -62,10 +67,9 @@ public class FileController {
|
|||||||
private static final long MAX_FILE_SIZE_10MB = 10485760L;
|
private static final long MAX_FILE_SIZE_10MB = 10485760L;
|
||||||
|
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
|
|
||||||
private final SysFileService sysFileService;
|
private final SysFileService sysFileService;
|
||||||
|
|
||||||
private final SysPictureService sysPictureService;
|
private final SysPictureService sysPictureService;
|
||||||
|
private final StorageServiceFactory storageServiceFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fileSize 文件大小
|
* @param fileSize 文件大小
|
||||||
@ -80,23 +84,61 @@ public class FileController {
|
|||||||
// 文件预检查
|
// 文件预检查
|
||||||
validateUploadFile(file, fileSize, folderName);
|
validateUploadFile(file, fileSize, folderName);
|
||||||
|
|
||||||
Map<String, Object> fileMap = UploadUtil.coderSingleFile(file, basePath + "/" + folderName + "/"+ CoderLoginUtil.getLoginName() + "/", fileSize);
|
Map<String, Object> fileMap;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 根据配置选择存储服务
|
||||||
|
StorageService storageService = storageServiceFactory.getStorageService(storageType);
|
||||||
|
|
||||||
|
// 生成唯一文件名
|
||||||
|
String fileName = OssUtil.generateUniqueFileName(file.getOriginalFilename());
|
||||||
|
|
||||||
|
// 构建文件夹路径
|
||||||
|
String folderPath = OssUtil.buildFolderPath(folderName, CoderLoginUtil.getLoginName());
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
fileMap = storageService.uploadFile(file, fileName, folderPath);
|
||||||
|
|
||||||
|
log.info("文件上传成功: storageType={}, fileName={}, filePath={}",
|
||||||
|
storageType, fileName, fileMap.get("filePath"));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("文件上传失败,尝试使用本地存储降级", e);
|
||||||
|
|
||||||
|
// 降级到本地存储
|
||||||
|
try {
|
||||||
|
StorageService localService = storageServiceFactory.getStorageService("local");
|
||||||
|
String fileName = OssUtil.generateUniqueFileName(file.getOriginalFilename());
|
||||||
|
String folderPath = OssUtil.buildFolderPath(folderName, CoderLoginUtil.getLoginName());
|
||||||
|
fileMap = localService.uploadFile(file, fileName, folderPath);
|
||||||
|
|
||||||
|
log.warn("使用本地存储降级成功: fileName={}", fileName);
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("本地存储降级也失败", ex);
|
||||||
|
throw new BusinessException(500, "文件上传失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取实际使用的存储服务类型
|
||||||
|
String actualStorageType = determineActualStorageType(fileMap);
|
||||||
|
|
||||||
// 统一保存到文件表,便于文件管理页面统一显示
|
// 统一保存到文件表,便于文件管理页面统一显示
|
||||||
saveUploadFilesInformation(fileMap, CoderConstants.TRUE);
|
saveUploadFilesInformation(fileMap, actualStorageType, CoderConstants.TRUE);
|
||||||
|
|
||||||
// 如果是图片,同时保存到图库表(保持图库管理功能)
|
// 如果是图片,同时保存到图库表(保持图库管理功能)
|
||||||
if (CoderConstants.PICTURES.equals(folderName)) {
|
if (CoderConstants.PICTURES.equals(folderName)) {
|
||||||
saveUploadPicturesInformation(fileMap, fileParam, CoderConstants.TRUE);
|
saveUploadPicturesInformation(fileMap, fileParam, actualStorageType, CoderConstants.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileMap;
|
return fileMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description [保存上传文件信息]
|
* @description [保存上传图片信息]
|
||||||
* @author Leocoder
|
* @author Leocoder
|
||||||
*/
|
*/
|
||||||
private void saveUploadPicturesInformation(Map<String, Object> fileMap, String fileParam, boolean isCreateBy) {
|
private void saveUploadPicturesInformation(Map<String, Object> fileMap, String fileParam, String storageServiceType, boolean isCreateBy) {
|
||||||
log.info("图库上传 ->");
|
log.info("图库上传 ->");
|
||||||
// 新增文件信息
|
// 新增文件信息
|
||||||
SysPicture sysPicture = new SysPicture();
|
SysPicture sysPicture = new SysPicture();
|
||||||
@ -105,16 +147,26 @@ public class FileController {
|
|||||||
sysPicture.setPictureSize(fileMap.get("fileSize").toString());
|
sysPicture.setPictureSize(fileMap.get("fileSize").toString());
|
||||||
sysPicture.setPictureSuffix(fileMap.get("suffixName").toString());
|
sysPicture.setPictureSuffix(fileMap.get("suffixName").toString());
|
||||||
sysPicture.setPictureUpload(fileMap.get("filePath").toString());
|
sysPicture.setPictureUpload(fileMap.get("filePath").toString());
|
||||||
sysPicture.setPictureService(CoderConstants.ONE_STRING);
|
sysPicture.setPictureService(storageServiceType);
|
||||||
|
|
||||||
|
// 设置文件访问路径
|
||||||
|
String fileUploadPath = (String) fileMap.get("fileUploadPath");
|
||||||
|
if (isFullUrl(fileUploadPath)) {
|
||||||
|
// 如果已经是完整URL(如OSS),直接使用
|
||||||
|
sysPicture.setPicturePath(fileUploadPath);
|
||||||
|
} else {
|
||||||
|
// 如果是相对路径(如本地存储),构建完整URL
|
||||||
String protocol = IpUtil.getProtocol(ServletUtil.getRequest());
|
String protocol = IpUtil.getProtocol(ServletUtil.getRequest());
|
||||||
if (StringUtils.isBlank(protocol)) {
|
if (StringUtils.isBlank(protocol)) {
|
||||||
protocol = "http";
|
protocol = "http";
|
||||||
}
|
}
|
||||||
String hostIp = IpUtil.getHostIp(ServletUtil.getRequest());
|
String hostIp = IpUtil.getHostIp(ServletUtil.getRequest());
|
||||||
String hostPort = StringUtils.isNotBlank(env.getProperty("server.port")) ? env.getProperty("server.port") : "18099";
|
String hostPort = StringUtils.isNotBlank(env.getProperty("server.port")) ? env.getProperty("server.port") : "18099";
|
||||||
log.info("IP地址:{},端口号:{}", hostIp, env.getProperty("server.port"));
|
sysPicture.setPicturePath(protocol + "://" + hostIp + ":" + hostPort + fileUploadPath);
|
||||||
sysPicture.setPicturePath(protocol + "://" + hostIp + ":" + hostPort + fileMap.get("fileUploadPath").toString());
|
}
|
||||||
|
|
||||||
log.info("图片回显地址:{}", sysPicture.getPicturePath());
|
log.info("图片回显地址:{}", sysPicture.getPicturePath());
|
||||||
|
|
||||||
if (CoderConstants.MINUS_ONE_STRING.equals(fileParam)) {
|
if (CoderConstants.MINUS_ONE_STRING.equals(fileParam)) {
|
||||||
sysPicture.setPictureType("9");
|
sysPicture.setPictureType("9");
|
||||||
} else {
|
} else {
|
||||||
@ -130,25 +182,35 @@ public class FileController {
|
|||||||
* @description [保存上传文件信息]
|
* @description [保存上传文件信息]
|
||||||
* @author Leocoder
|
* @author Leocoder
|
||||||
*/
|
*/
|
||||||
private void saveUploadFilesInformation(Map<String, Object> fileMap, boolean isCreateBy) {
|
private void saveUploadFilesInformation(Map<String, Object> fileMap, String storageServiceType, boolean isCreateBy) {
|
||||||
log.info("文件上传 ->");
|
log.info("文件上传 ->");
|
||||||
// 新增图库信息
|
// 新增文件信息
|
||||||
SysFile sysFile = new SysFile();
|
SysFile sysFile = new SysFile();
|
||||||
sysFile.setFileName(fileMap.get("fileName").toString());
|
sysFile.setFileName(fileMap.get("fileName").toString());
|
||||||
sysFile.setNewName(fileMap.get("newName").toString());
|
sysFile.setNewName(fileMap.get("newName").toString());
|
||||||
sysFile.setFileSize(fileMap.get("fileSize").toString());
|
sysFile.setFileSize(fileMap.get("fileSize").toString());
|
||||||
sysFile.setFileSuffix(fileMap.get("suffixName").toString());
|
sysFile.setFileSuffix(fileMap.get("suffixName").toString());
|
||||||
sysFile.setFileUpload(fileMap.get("filePath").toString());
|
sysFile.setFileUpload(fileMap.get("filePath").toString());
|
||||||
sysFile.setFileService(CoderConstants.ONE_STRING);
|
sysFile.setFileService(storageServiceType);
|
||||||
|
|
||||||
|
// 设置文件访问路径
|
||||||
|
String fileUploadPath = (String) fileMap.get("fileUploadPath");
|
||||||
|
if (isFullUrl(fileUploadPath)) {
|
||||||
|
// 如果已经是完整URL(如OSS),直接使用
|
||||||
|
sysFile.setFilePath(fileUploadPath);
|
||||||
|
} else {
|
||||||
|
// 如果是相对路径(如本地存储),构建完整URL
|
||||||
String protocol = IpUtil.getProtocol(ServletUtil.getRequest());
|
String protocol = IpUtil.getProtocol(ServletUtil.getRequest());
|
||||||
if (StringUtils.isBlank(protocol)) {
|
if (StringUtils.isBlank(protocol)) {
|
||||||
protocol = "http";
|
protocol = "http";
|
||||||
}
|
}
|
||||||
String hostIp = IpUtil.getHostIp(ServletUtil.getRequest());
|
String hostIp = IpUtil.getHostIp(ServletUtil.getRequest());
|
||||||
String hostPort = StringUtils.isNotBlank(env.getProperty("server.port")) ? env.getProperty("server.port") : "18088";
|
String hostPort = StringUtils.isNotBlank(env.getProperty("server.port")) ? env.getProperty("server.port") : "18099";
|
||||||
log.info("IP地址:{},端口号:{}", hostIp, env.getProperty("server.port"));
|
sysFile.setFilePath(protocol + "://" + hostIp + ":" + hostPort + fileUploadPath);
|
||||||
sysFile.setFilePath(protocol + "://" + hostIp + ":" + hostPort + fileMap.get("fileUploadPath").toString());
|
}
|
||||||
|
|
||||||
log.info("文件回显地址:{}", sysFile.getFilePath());
|
log.info("文件回显地址:{}", sysFile.getFilePath());
|
||||||
|
|
||||||
String fileType = FileTypeUtil.checkFileExtension(fileMap.get("suffixName").toString());
|
String fileType = FileTypeUtil.checkFileExtension(fileMap.get("suffixName").toString());
|
||||||
sysFile.setFileType(fileType);
|
sysFile.setFileType(fileType);
|
||||||
if (isCreateBy) {
|
if (isCreateBy) {
|
||||||
@ -157,6 +219,33 @@ public class FileController {
|
|||||||
sysFileService.save(sysFile);
|
sysFileService.save(sysFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为完整URL
|
||||||
|
*/
|
||||||
|
private boolean isFullUrl(String url) {
|
||||||
|
return StringUtils.isNotBlank(url) && (url.startsWith("http://") || url.startsWith("https://"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定实际使用的存储服务类型
|
||||||
|
*/
|
||||||
|
private String determineActualStorageType(Map<String, Object> fileMap) {
|
||||||
|
String fileUploadPath = (String) fileMap.get("fileUploadPath");
|
||||||
|
|
||||||
|
// 根据返回的文件路径判断实际使用的存储类型
|
||||||
|
if (isFullUrl(fileUploadPath)) {
|
||||||
|
// 如果是完整URL,检查是否包含OSS域名
|
||||||
|
if (fileUploadPath.contains(".aliyuncs.com") || fileUploadPath.contains("oss-")) {
|
||||||
|
return "3"; // OSS
|
||||||
|
} else if (fileUploadPath.contains("minio") || fileUploadPath.contains(":9000")) {
|
||||||
|
return "2"; // MinIO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认为本地存储
|
||||||
|
return "1"; // Local
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fileSize 文件大小
|
* @param fileSize 文件大小
|
||||||
* @param folderName 上传指定文件夹名称
|
* @param folderName 上传指定文件夹名称
|
||||||
@ -171,15 +260,53 @@ public class FileController {
|
|||||||
// 文件预检查
|
// 文件预检查
|
||||||
validateUploadFile(file, fileSize, folderName);
|
validateUploadFile(file, fileSize, folderName);
|
||||||
|
|
||||||
Map<String, Object> fileMap = UploadUtil.coderSingleFile(file, basePath + "/" + folderName + "/", fileSize);
|
Map<String, Object> fileMap;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 根据配置选择存储服务
|
||||||
|
StorageService storageService = storageServiceFactory.getStorageService(storageType);
|
||||||
|
|
||||||
|
// 生成唯一文件名
|
||||||
|
String fileName = OssUtil.generateUniqueFileName(file.getOriginalFilename());
|
||||||
|
|
||||||
|
// 构建文件夹路径(匿名上传不包含用户名)
|
||||||
|
String folderPath = folderName;
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
fileMap = storageService.uploadFile(file, fileName, folderPath);
|
||||||
|
|
||||||
|
log.info("匿名文件上传成功: storageType={}, fileName={}, filePath={}",
|
||||||
|
storageType, fileName, fileMap.get("filePath"));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("匿名文件上传失败,尝试使用本地存储降级", e);
|
||||||
|
|
||||||
|
// 降级到本地存储
|
||||||
|
try {
|
||||||
|
StorageService localService = storageServiceFactory.getStorageService("local");
|
||||||
|
String fileName = OssUtil.generateUniqueFileName(file.getOriginalFilename());
|
||||||
|
String folderPath = folderName;
|
||||||
|
fileMap = localService.uploadFile(file, fileName, folderPath);
|
||||||
|
|
||||||
|
log.warn("匿名上传使用本地存储降级成功: fileName={}", fileName);
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("本地存储降级也失败", ex);
|
||||||
|
throw new BusinessException(500, "文件上传失败: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取实际使用的存储服务类型
|
||||||
|
String actualStorageType = determineActualStorageType(fileMap);
|
||||||
|
|
||||||
// 统一保存到文件表
|
// 统一保存到文件表
|
||||||
saveUploadFilesInformation(fileMap, false);
|
saveUploadFilesInformation(fileMap, actualStorageType, false);
|
||||||
|
|
||||||
// 如果是图片,同时保存到图库表
|
// 如果是图片,同时保存到图库表
|
||||||
if (CoderConstants.PICTURES.equals(folderName)) {
|
if (CoderConstants.PICTURES.equals(folderName)) {
|
||||||
saveUploadPicturesInformation(fileMap, fileParam, false);
|
saveUploadPicturesInformation(fileMap, fileParam, actualStorageType, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileMap;
|
return fileMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.leocoder.thin.common.constants.CoderConstants;
|
import org.leocoder.thin.common.constants.CoderConstants;
|
||||||
import org.leocoder.thin.common.exception.coder.YUtil;
|
import org.leocoder.thin.common.exception.coder.YUtil;
|
||||||
@ -17,6 +18,8 @@ import org.leocoder.thin.domain.enums.oper.OperType;
|
|||||||
import org.leocoder.thin.domain.model.vo.system.SysFileVo;
|
import org.leocoder.thin.domain.model.vo.system.SysFileVo;
|
||||||
import org.leocoder.thin.domain.pojo.system.SysFile;
|
import org.leocoder.thin.domain.pojo.system.SysFile;
|
||||||
import org.leocoder.thin.operlog.annotation.OperLog;
|
import org.leocoder.thin.operlog.annotation.OperLog;
|
||||||
|
import org.leocoder.thin.oss.service.StorageService;
|
||||||
|
import org.leocoder.thin.oss.service.StorageServiceFactory;
|
||||||
import org.leocoder.thin.system.service.file.SysFileService;
|
import org.leocoder.thin.system.service.file.SysFileService;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -33,9 +36,12 @@ import java.util.List;
|
|||||||
@RequestMapping("/coder")
|
@RequestMapping("/coder")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
|
@Slf4j
|
||||||
public class SysFileController {
|
public class SysFileController {
|
||||||
|
|
||||||
|
|
||||||
private final SysFileService sysFileService;
|
private final SysFileService sysFileService;
|
||||||
|
private final StorageServiceFactory storageServiceFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description [分页查询]
|
* @description [分页查询]
|
||||||
@ -124,8 +130,8 @@ public class SysFileController {
|
|||||||
SysFile sysFile = sysFileService.getById(id);
|
SysFile sysFile = sysFileService.getById(id);
|
||||||
if (!ObjectUtil.isEmpty(sysFile)) {
|
if (!ObjectUtil.isEmpty(sysFile)) {
|
||||||
if (StringUtils.isNotBlank(sysFile.getFileUpload())) {
|
if (StringUtils.isNotBlank(sysFile.getFileUpload())) {
|
||||||
// 删除文件
|
// 根据存储服务类型删除文件
|
||||||
FileUtil.deleteFile(sysFile.getFileUpload());
|
deletePhysicalFile(sysFile.getFileUpload(), sysFile.getFileService());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
YUtil.isTrue(!sysFileService.removeById(id), "删除失败,请稍后重试");
|
YUtil.isTrue(!sysFileService.removeById(id), "删除失败,请稍后重试");
|
||||||
@ -147,12 +153,58 @@ public class SysFileController {
|
|||||||
if (!sysFileList.isEmpty()) {
|
if (!sysFileList.isEmpty()) {
|
||||||
for (SysFile sysFile : sysFileList) {
|
for (SysFile sysFile : sysFileList) {
|
||||||
if (StringUtils.isNotBlank(sysFile.getFileUpload())) {
|
if (StringUtils.isNotBlank(sysFile.getFileUpload())) {
|
||||||
// 删除文件
|
// 根据存储服务类型删除文件
|
||||||
FileUtil.deleteFile(sysFile.getFileUpload());
|
deletePhysicalFile(sysFile.getFileUpload(), sysFile.getFileService());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
YUtil.isTrue(!sysFileService.removeBatchByIds(ids), "删除失败,请稍后重试");
|
YUtil.isTrue(!sysFileService.removeBatchByIds(ids), "删除失败,请稍后重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据存储服务类型删除物理文件
|
||||||
|
*/
|
||||||
|
private void deletePhysicalFile(String filePath, String fileService) {
|
||||||
|
try {
|
||||||
|
boolean deleteResult = false;
|
||||||
|
|
||||||
|
switch (fileService) {
|
||||||
|
case "1": // 本地存储
|
||||||
|
deleteResult = FileUtil.deleteFile(filePath);
|
||||||
|
break;
|
||||||
|
case "2": // MinIO存储
|
||||||
|
try {
|
||||||
|
StorageService minioService = storageServiceFactory.getStorageService("minio");
|
||||||
|
deleteResult = minioService.deleteFile(filePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("MinIO存储服务不可用,尝试本地删除", e);
|
||||||
|
deleteResult = FileUtil.deleteFile(filePath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "3": // OSS存储
|
||||||
|
try {
|
||||||
|
StorageService ossService = storageServiceFactory.getStorageService("oss");
|
||||||
|
deleteResult = ossService.deleteFile(filePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("OSS存储服务不可用,尝试本地删除", e);
|
||||||
|
deleteResult = FileUtil.deleteFile(filePath);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.warn("未知的存储服务类型: {},使用本地删除", fileService);
|
||||||
|
deleteResult = FileUtil.deleteFile(filePath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteResult) {
|
||||||
|
log.info("文件删除成功: filePath={}, fileService={}", filePath, fileService);
|
||||||
|
} else {
|
||||||
|
log.warn("文件删除失败: filePath={}, fileService={}", filePath, fileService);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除物理文件时发生异常: filePath={}, fileService={}", filePath, fileService, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.leocoder.thin.common.constants.CoderConstants;
|
import org.leocoder.thin.common.constants.CoderConstants;
|
||||||
import org.leocoder.thin.common.exception.coder.YUtil;
|
import org.leocoder.thin.common.exception.coder.YUtil;
|
||||||
@ -15,6 +16,8 @@ import org.leocoder.thin.domain.enums.oper.OperType;
|
|||||||
import org.leocoder.thin.domain.model.vo.system.SysPictureVo;
|
import org.leocoder.thin.domain.model.vo.system.SysPictureVo;
|
||||||
import org.leocoder.thin.domain.pojo.system.SysPicture;
|
import org.leocoder.thin.domain.pojo.system.SysPicture;
|
||||||
import org.leocoder.thin.operlog.annotation.OperLog;
|
import org.leocoder.thin.operlog.annotation.OperLog;
|
||||||
|
import org.leocoder.thin.oss.service.StorageService;
|
||||||
|
import org.leocoder.thin.oss.service.StorageServiceFactory;
|
||||||
import org.leocoder.thin.system.service.picture.SysPictureService;
|
import org.leocoder.thin.system.service.picture.SysPictureService;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@ -31,9 +34,11 @@ import java.util.List;
|
|||||||
@RequestMapping("/coder")
|
@RequestMapping("/coder")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
|
@Slf4j
|
||||||
public class SysPictureController {
|
public class SysPictureController {
|
||||||
|
|
||||||
private final SysPictureService sysPictureService;
|
private final SysPictureService sysPictureService;
|
||||||
|
private final StorageServiceFactory storageServiceFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description [分页查询]
|
* @description [分页查询]
|
||||||
@ -117,6 +122,13 @@ public class SysPictureController {
|
|||||||
@PostMapping("/sysPicture/deleteById/{id}")
|
@PostMapping("/sysPicture/deleteById/{id}")
|
||||||
@OperLog(value = "删除图库", operType = OperType.DELETE)
|
@OperLog(value = "删除图库", operType = OperType.DELETE)
|
||||||
public void delete(@PathVariable("id") Long id) {
|
public void delete(@PathVariable("id") Long id) {
|
||||||
|
SysPicture sysPicture = sysPictureService.getById(id);
|
||||||
|
if (sysPicture != null) {
|
||||||
|
if (StringUtils.isNotBlank(sysPicture.getPictureUpload())) {
|
||||||
|
// 根据存储服务类型删除图片文件
|
||||||
|
deletePhysicalFile(sysPicture.getPictureUpload(), sysPicture.getPictureService());
|
||||||
|
}
|
||||||
|
}
|
||||||
YUtil.isTrue(!sysPictureService.removeById(id), "删除失败,请稍后重试");
|
YUtil.isTrue(!sysPictureService.removeById(id), "删除失败,请稍后重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +142,63 @@ public class SysPictureController {
|
|||||||
@PostMapping("/sysPicture/batchDelete")
|
@PostMapping("/sysPicture/batchDelete")
|
||||||
@OperLog(value = "批量删除图库", operType = OperType.DELETE)
|
@OperLog(value = "批量删除图库", operType = OperType.DELETE)
|
||||||
public void batchDelete(@RequestBody List<Long> ids) {
|
public void batchDelete(@RequestBody List<Long> ids) {
|
||||||
|
List<SysPicture> sysPictureList = sysPictureService.listByIds(ids);
|
||||||
|
|
||||||
|
// 批量删除物理文件
|
||||||
|
for (SysPicture sysPicture : sysPictureList) {
|
||||||
|
if (StringUtils.isNotBlank(sysPicture.getPictureUpload())) {
|
||||||
|
deletePhysicalFile(sysPicture.getPictureUpload(), sysPicture.getPictureService());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
YUtil.isTrue(!sysPictureService.removeBatchByIds(ids), "删除失败,请稍后重试");
|
YUtil.isTrue(!sysPictureService.removeBatchByIds(ids), "删除失败,请稍后重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据存储服务类型删除物理文件
|
||||||
|
*/
|
||||||
|
private void deletePhysicalFile(String filePath, String fileService) {
|
||||||
|
try {
|
||||||
|
boolean deleteResult = false;
|
||||||
|
|
||||||
|
switch (fileService) {
|
||||||
|
case "1": // 本地存储
|
||||||
|
// 对于本地存储,需要使用完整路径
|
||||||
|
deleteResult = new java.io.File(filePath).delete();
|
||||||
|
break;
|
||||||
|
case "2": // MinIO存储
|
||||||
|
try {
|
||||||
|
StorageService minioService = storageServiceFactory.getStorageService("minio");
|
||||||
|
deleteResult = minioService.deleteFile(filePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("MinIO存储服务不可用,跳过删除", e);
|
||||||
|
deleteResult = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "3": // OSS存储
|
||||||
|
try {
|
||||||
|
StorageService ossService = storageServiceFactory.getStorageService("oss");
|
||||||
|
deleteResult = ossService.deleteFile(filePath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("OSS存储服务不可用,跳过删除", e);
|
||||||
|
deleteResult = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.warn("未知的存储服务类型: {},跳过文件删除", fileService);
|
||||||
|
deleteResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteResult) {
|
||||||
|
log.info("图片文件删除成功: filePath={}, fileService={}", filePath, fileService);
|
||||||
|
} else {
|
||||||
|
log.warn("图片文件删除失败: filePath={}, fileService={}", filePath, fileService);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除物理图片文件时发生异常: filePath={}, fileService={}", filePath, fileService, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user