系统配置修改
This commit is contained in:
@@ -29,6 +29,10 @@
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-file</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package org.xyzh.file.config;
|
||||
|
||||
import io.minio.MinioClient;
|
||||
import lombok.Data;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.api.system.service.SysConfigService;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @description MinIO 配置类,从数据库读取配置信息
|
||||
* @filename MinioConfig.java
|
||||
* @author yslg
|
||||
* @copyright yslg
|
||||
* @since 2025-12-09
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
public class MinioConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MinioConfig.class);
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
// MinIO 配置项的键名
|
||||
private static final String MINIO_ENDPOINT_KEY = "minio.endpoint";
|
||||
private static final String MINIO_ACCESS_KEY = "minio.accessKey";
|
||||
private static final String MINIO_SECRET_KEY = "minio.secretKey";
|
||||
private static final String MINIO_BUCKET_NAME_KEY = "minio.bucketName";
|
||||
private static final String MINIO_PUBLIC_URL_KEY = "minio.publicUrl";
|
||||
|
||||
// 默认值
|
||||
private static final String DEFAULT_ENDPOINT = "http://localhost:9000";
|
||||
private static final String DEFAULT_ACCESS_KEY = "minioadmin";
|
||||
private static final String DEFAULT_SECRET_KEY = "minioadmin123";
|
||||
private static final String DEFAULT_BUCKET_NAME = "urban-lifeline";
|
||||
private static final String DEFAULT_PUBLIC_URL = "http://localhost:9000";
|
||||
|
||||
// 配置属性
|
||||
private String endpoint;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucketName;
|
||||
private String publicUrl;
|
||||
|
||||
private MinioClient minioClient;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
loadConfig();
|
||||
initClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库加载MinIO配置
|
||||
*/
|
||||
private void loadConfig() {
|
||||
try {
|
||||
endpoint = sysConfigService.getStringConfig(MINIO_ENDPOINT_KEY);
|
||||
accessKey = sysConfigService.getStringConfig(MINIO_ACCESS_KEY);
|
||||
secretKey = sysConfigService.getStringConfig(MINIO_SECRET_KEY);
|
||||
bucketName = sysConfigService.getStringConfig(MINIO_BUCKET_NAME_KEY);
|
||||
publicUrl = sysConfigService.getStringConfig(MINIO_PUBLIC_URL_KEY);
|
||||
|
||||
// 使用默认值如果配置不存在
|
||||
if (endpoint == null || endpoint.trim().isEmpty()) {
|
||||
endpoint = DEFAULT_ENDPOINT;
|
||||
logger.warn("未找到MinIO endpoint配置,使用默认值: {}", DEFAULT_ENDPOINT);
|
||||
}
|
||||
if (accessKey == null || accessKey.trim().isEmpty()) {
|
||||
accessKey = DEFAULT_ACCESS_KEY;
|
||||
logger.warn("未找到MinIO accessKey配置,使用默认值");
|
||||
}
|
||||
if (secretKey == null || secretKey.trim().isEmpty()) {
|
||||
secretKey = DEFAULT_SECRET_KEY;
|
||||
logger.warn("未找到MinIO secretKey配置,使用默认值");
|
||||
}
|
||||
if (bucketName == null || bucketName.trim().isEmpty()) {
|
||||
bucketName = DEFAULT_BUCKET_NAME;
|
||||
logger.warn("未找到MinIO bucketName配置,使用默认值: {}", DEFAULT_BUCKET_NAME);
|
||||
}
|
||||
if (publicUrl == null || publicUrl.trim().isEmpty()) {
|
||||
publicUrl = endpoint; // 默认使用endpoint作为公网访问地址
|
||||
logger.warn("未找到MinIO publicUrl配置,使用endpoint作为默认值: {}", endpoint);
|
||||
}
|
||||
|
||||
logger.info("MinIO配置加载完成 - endpoint: {}, bucketName: {}", endpoint, bucketName);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("加载MinIO配置失败,使用默认配置", e);
|
||||
endpoint = DEFAULT_ENDPOINT;
|
||||
accessKey = DEFAULT_ACCESS_KEY;
|
||||
secretKey = DEFAULT_SECRET_KEY;
|
||||
bucketName = DEFAULT_BUCKET_NAME;
|
||||
publicUrl = DEFAULT_PUBLIC_URL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化MinIO客户端
|
||||
*/
|
||||
private void initClient() {
|
||||
try {
|
||||
minioClient = MinioClient.builder()
|
||||
.endpoint(endpoint)
|
||||
.credentials(accessKey, secretKey)
|
||||
.build();
|
||||
|
||||
logger.info("MinIO客户端初始化成功");
|
||||
} catch (Exception e) {
|
||||
logger.error("MinIO客户端初始化失败", e);
|
||||
throw new RuntimeException("MinIO客户端初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载配置
|
||||
*/
|
||||
public void reloadConfig() {
|
||||
logger.info("重新加载MinIO配置");
|
||||
loadConfig();
|
||||
initClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MinIO客户端
|
||||
*/
|
||||
public MinioClient getMinioClient() {
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建文件的公网访问URL
|
||||
* @param objectName 对象名称
|
||||
* @return 完整的文件访问URL
|
||||
*/
|
||||
public String buildFileUrl(String objectName) {
|
||||
return publicUrl + "/" + bucketName + "/" + objectName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.xyzh.file.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
import org.xyzh.api.file.dto.TbSysFileDTO;
|
||||
|
||||
/**
|
||||
* @description 文件Mapper接口
|
||||
* @filename FileMapper.java
|
||||
* @author yslg
|
||||
* @copyright yslg
|
||||
* @since 2025-12-09
|
||||
*/
|
||||
@Mapper
|
||||
public interface FileMapper extends BaseMapper<TbSysFileDTO> {
|
||||
|
||||
/**
|
||||
* 根据文件ID查询文件信息(自定义方法替代selectById)
|
||||
* @param fileId 文件ID
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Select("SELECT * FROM file.tb_sys_file WHERE file_id = #{fileId} AND deleted = 0")
|
||||
TbSysFileDTO selectByFileId(@Param("fileId") String fileId);
|
||||
|
||||
/**
|
||||
* 插入文件记录(自定义方法)
|
||||
* @param fileDTO 文件DTO
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insertFile(TbSysFileDTO fileDTO);
|
||||
|
||||
/**
|
||||
* 根据文件ID更新文件信息(自定义方法替代updateById)
|
||||
* @param fileDTO 文件DTO
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateByFileId(TbSysFileDTO fileDTO);
|
||||
|
||||
/**
|
||||
* 根据文件ID逻辑删除文件(自定义方法)
|
||||
* @param fileId 文件ID
|
||||
* @param updater 更新者
|
||||
* @return 影响行数
|
||||
*/
|
||||
@Update("UPDATE file.tb_sys_file SET deleted = 1, delete_time = CURRENT_TIMESTAMP, updater = #{updater} WHERE file_id = #{fileId}")
|
||||
int deleteByFileId(@Param("fileId") String fileId, @Param("updater") String updater);
|
||||
|
||||
/**
|
||||
* 根据模块和业务ID查询文件列表
|
||||
* @param module 模块
|
||||
* @param businessId 业务ID
|
||||
* @return 文件列表
|
||||
*/
|
||||
@Select("SELECT * FROM file.tb_sys_file WHERE module = #{module} AND business_id = #{businessId} AND deleted = 0 ORDER BY create_time DESC")
|
||||
List<TbSysFileDTO> selectByModuleAndBusinessId(@Param("module") String module, @Param("businessId") String businessId);
|
||||
|
||||
/**
|
||||
* 根据上传者查询文件列表
|
||||
* @param uploader 上传者用户ID
|
||||
* @return 文件列表
|
||||
*/
|
||||
@Select("SELECT * FROM file.tb_sys_file WHERE uploader = #{uploader} AND deleted = 0 ORDER BY create_time DESC")
|
||||
List<TbSysFileDTO> selectByUploader(@Param("uploader") String uploader);
|
||||
|
||||
/**
|
||||
* 根据MD5查询文件(用于防重复上传)
|
||||
* @param md5Hash MD5哈希值
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Select("SELECT * FROM file.tb_sys_file WHERE md5_hash = #{md5Hash} AND deleted = 0 LIMIT 1")
|
||||
TbSysFileDTO selectByMd5Hash(@Param("md5Hash") String md5Hash);
|
||||
|
||||
/**
|
||||
* 根据MinIO对象名称查询文件
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return 文件信息
|
||||
*/
|
||||
@Select("SELECT * FROM file.tb_sys_file WHERE bucket_name = #{bucketName} AND object_name = #{objectName} AND deleted = 0")
|
||||
TbSysFileDTO selectByMinioObject(@Param("bucketName") String bucketName, @Param("objectName") String objectName);
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package org.xyzh.file.service.impl;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
// import org.springframework.beans.BeanUtils; // 不再需要BeanUtils
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.xyzh.api.file.dto.TbSysFileDTO;
|
||||
import org.xyzh.api.file.service.FileService;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.file.config.MinioConfig;
|
||||
import org.xyzh.file.mapper.FileMapper;
|
||||
import org.xyzh.file.util.MinioUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @description 文件服务实现类
|
||||
* @filename FileServiceImpl.java
|
||||
* @author yslg
|
||||
* @copyright yslg
|
||||
* @since 2025-12-09
|
||||
*/
|
||||
@DubboService(
|
||||
version = "1.0.0",
|
||||
group = "file",
|
||||
timeout = 3000,
|
||||
retries = 0
|
||||
)
|
||||
public class FileServiceImpl implements FileService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private MinioConfig minioConfig;
|
||||
|
||||
@Autowired
|
||||
private MinioUtil minioUtil;
|
||||
|
||||
@Autowired
|
||||
private FileMapper fileMapper;
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbSysFileDTO> uploadFile(MultipartFile file, String module, String businessId) {
|
||||
try {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return ResultDomain.failure("文件不能为空");
|
||||
}
|
||||
|
||||
// 生成文件信息
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = getFileExtension(originalFilename);
|
||||
String contentType = file.getContentType();
|
||||
long size = file.getSize();
|
||||
|
||||
// 生成唯一的对象名称
|
||||
String objectName = generateObjectName(originalFilename, module);
|
||||
|
||||
// 计算文件MD5
|
||||
String md5Hash = calculateMD5(file.getBytes());
|
||||
|
||||
// 上传到MinIO
|
||||
String bucketName = minioConfig.getBucketName();
|
||||
boolean uploadSuccess = minioUtil.uploadFile(
|
||||
bucketName,
|
||||
objectName,
|
||||
file.getInputStream(),
|
||||
size,
|
||||
contentType
|
||||
);
|
||||
|
||||
if (!uploadSuccess) {
|
||||
return ResultDomain.failure("文件上传到MinIO失败");
|
||||
}
|
||||
|
||||
// 构建文件访问URL
|
||||
String fileUrl = minioConfig.buildFileUrl(objectName);
|
||||
|
||||
// 保存到数据库
|
||||
TbSysFileDTO fileDTO = new TbSysFileDTO();
|
||||
fileDTO.setOptsn(UUID.randomUUID().toString());
|
||||
fileDTO.setFileId(UUID.randomUUID().toString());
|
||||
fileDTO.setName(originalFilename);
|
||||
fileDTO.setPath(objectName);
|
||||
fileDTO.setSize(size);
|
||||
fileDTO.setType(extension);
|
||||
fileDTO.setStorageType("MINIO");
|
||||
fileDTO.setMimeType(contentType);
|
||||
fileDTO.setUrl(fileUrl);
|
||||
fileDTO.setStatus("NORMAL");
|
||||
fileDTO.setModule(module);
|
||||
fileDTO.setBusinessId(businessId);
|
||||
fileDTO.setObjectName(objectName);
|
||||
fileDTO.setBucketName(bucketName);
|
||||
fileDTO.setMd5Hash(md5Hash);
|
||||
fileDTO.setExtension(extension);
|
||||
fileDTO.setCreateTime(new java.util.Date());
|
||||
|
||||
int result = fileMapper.insertFile(fileDTO);
|
||||
if (result <= 0) {
|
||||
// 如果数据库保存失败,删除MinIO中的文件
|
||||
minioUtil.deleteFile(bucketName, objectName);
|
||||
return ResultDomain.failure("文件信息保存失败");
|
||||
}
|
||||
|
||||
logger.info("文件上传成功: {}, 大小: {} bytes", originalFilename, size);
|
||||
return ResultDomain.success("文件上传成功", fileDTO);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件上传失败", e);
|
||||
return ResultDomain.failure("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbSysFileDTO> batchUploadFiles(MultipartFile[] files, String module, String businessId, String uploader) {
|
||||
try {
|
||||
if (files == null || files.length == 0) {
|
||||
return ResultDomain.failure("文件列表不能为空");
|
||||
}
|
||||
|
||||
List<TbSysFileDTO> uploadedFiles = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
ResultDomain<TbSysFileDTO> result = uploadFile(file, module, businessId);
|
||||
if (result.getSuccess()) {
|
||||
TbSysFileDTO fileDTO = result.getData();
|
||||
fileDTO.setUploader(uploader);
|
||||
uploadedFiles.add(fileDTO);
|
||||
} else {
|
||||
// 如果有文件上传失败,记录日志但继续处理其他文件
|
||||
logger.warn("文件上传失败: {}, 原因: {}", file.getOriginalFilename(), result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadedFiles.isEmpty()) {
|
||||
return ResultDomain.failure("所有文件上传失败");
|
||||
}
|
||||
|
||||
// 返回上传成功的文件列表(这里简化处理,实际可能需要返回详细结果)
|
||||
TbSysFileDTO resultDTO = new TbSysFileDTO();
|
||||
// 可以在这里设置批量上传的汇总信息
|
||||
|
||||
logger.info("批量文件上传完成,成功: {}, 总数: {}", uploadedFiles.size(), files.length);
|
||||
return ResultDomain.success("批量文件上传成功", resultDTO);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("批量文件上传失败", e);
|
||||
return ResultDomain.failure("批量文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<Boolean> deleteFile(String fileId) {
|
||||
try {
|
||||
// 从数据库获取文件信息
|
||||
TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
|
||||
if (sysFile == null) {
|
||||
return ResultDomain.failure("文件不存在");
|
||||
}
|
||||
|
||||
// 删除MinIO中的文件
|
||||
minioUtil.deleteFile(sysFile.getBucketName(), sysFile.getObjectName());
|
||||
|
||||
// 逻辑删除数据库记录
|
||||
int result = fileMapper.deleteByFileId(fileId, "system");
|
||||
|
||||
if (result > 0) {
|
||||
logger.info("文件删除成功: {}", fileId);
|
||||
return ResultDomain.success("文件删除成功", true);
|
||||
} else {
|
||||
return ResultDomain.failure("文件删除失败");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除失败: {}", fileId, e);
|
||||
return ResultDomain.failure("文件删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbSysFileDTO> batchDeleteFiles(String[] fileIds) {
|
||||
try {
|
||||
if (fileIds == null || fileIds.length == 0) {
|
||||
return ResultDomain.failure("文件ID列表不能为空");
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
for (String fileId : fileIds) {
|
||||
ResultDomain<Boolean> result = deleteFile(fileId);
|
||||
if (result.getSuccess()) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
TbSysFileDTO resultDTO = new TbSysFileDTO();
|
||||
// 可以在这里设置批量删除的结果统计
|
||||
|
||||
logger.info("批量文件删除完成,成功: {}, 总数: {}", successCount, fileIds.length);
|
||||
return ResultDomain.success("批量文件删除完成", resultDTO);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("批量文件删除失败", e);
|
||||
return ResultDomain.failure("批量文件删除失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<byte[]> downloadFile(String fileId) {
|
||||
try {
|
||||
// 从数据库获取文件信息
|
||||
TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
|
||||
if (sysFile == null) {
|
||||
return ResultDomain.failure("文件不存在");
|
||||
}
|
||||
|
||||
// 从MinIO下载文件
|
||||
InputStream inputStream = minioUtil.downloadFile(sysFile.getBucketName(), sysFile.getObjectName());
|
||||
if (inputStream == null) {
|
||||
return ResultDomain.failure("文件下载失败");
|
||||
}
|
||||
|
||||
// 将输入流转换为字节数组
|
||||
byte[] data = inputStream.readAllBytes();
|
||||
inputStream.close();
|
||||
|
||||
logger.info("文件下载成功: {}", fileId);
|
||||
return ResultDomain.success("文件下载成功", data);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件下载失败: {}", fileId, e);
|
||||
return ResultDomain.failure("文件下载失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbSysFileDTO> getFileById(String fileId) {
|
||||
try {
|
||||
TbSysFileDTO sysFile = fileMapper.selectByFileId(fileId);
|
||||
if (sysFile == null) {
|
||||
return ResultDomain.failure("文件不存在");
|
||||
}
|
||||
|
||||
return ResultDomain.success("获取文件信息成功", sysFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("查询文件失败: {}", fileId, e);
|
||||
return ResultDomain.failure("查询文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDomain<TbSysFileDTO> saveTempFile(MultipartFile file, String module, String businessId) {
|
||||
try {
|
||||
// 临时文件上传逻辑与普通上传相同,但可以添加特殊标识
|
||||
ResultDomain<TbSysFileDTO> result = uploadFile(file, module, businessId);
|
||||
if (result.getSuccess()) {
|
||||
TbSysFileDTO fileDTO = result.getData();
|
||||
fileDTO.setStatus("TEMP"); // 标记为临时文件
|
||||
|
||||
// 更新数据库中的状态
|
||||
fileMapper.updateByFileId(fileDTO);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("临时文件保存失败", e);
|
||||
return ResultDomain.failure("临时文件保存失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的对象名称
|
||||
*/
|
||||
private String generateObjectName(String originalFilename, String module) {
|
||||
String extension = getFileExtension(originalFilename);
|
||||
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
|
||||
return String.format("%s/%s/%s%s", module, date, uuid, extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*/
|
||||
private String getFileExtension(String filename) {
|
||||
if (filename == null || !filename.contains(".")) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf("."));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算文件MD5值
|
||||
*/
|
||||
private String calculateMD5(byte[] data) {
|
||||
return DigestUtils.md5DigestAsHex(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package org.xyzh.file.util;
|
||||
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xyzh.file.config.MinioConfig;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @description MinIO 工具类
|
||||
* @filename MinioUtil.java
|
||||
* @author yslg
|
||||
* @copyright yslg
|
||||
* @since 2025-12-09
|
||||
*/
|
||||
@Component
|
||||
public class MinioUtil {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MinioUtil.class);
|
||||
|
||||
@Autowired
|
||||
private MinioConfig minioConfig;
|
||||
|
||||
/**
|
||||
* 检查存储桶是否存在,如果不存在则创建
|
||||
* @param bucketName 存储桶名称
|
||||
* @return true 成功,false 失败
|
||||
*/
|
||||
public boolean createBucketIfNotExists(String bucketName) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
// 检查存储桶是否存在
|
||||
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.build());
|
||||
|
||||
if (!exists) {
|
||||
// 创建存储桶
|
||||
minioClient.makeBucket(MakeBucketArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.build());
|
||||
logger.info("成功创建存储桶: {}", bucketName);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("创建存储桶失败: {}", bucketName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到MinIO
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @param inputStream 文件输入流
|
||||
* @param size 文件大小
|
||||
* @param contentType 文件类型
|
||||
* @return true 成功,false 失败
|
||||
*/
|
||||
public boolean uploadFile(String bucketName, String objectName, InputStream inputStream, long size, String contentType) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
// 确保存储桶存在
|
||||
if (!createBucketIfNotExists(bucketName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
minioClient.putObject(PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.stream(inputStream, size, -1)
|
||||
.contentType(contentType)
|
||||
.build());
|
||||
|
||||
logger.info("文件上传成功: {}/{}", bucketName, objectName);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件上传失败: {}/{}", bucketName, objectName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传字节数组到MinIO
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @param data 字节数组
|
||||
* @param contentType 文件类型
|
||||
* @return true 成功,false 失败
|
||||
*/
|
||||
public boolean uploadFile(String bucketName, String objectName, byte[] data, String contentType) {
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data)) {
|
||||
return uploadFile(bucketName, objectName, inputStream, data.length, contentType);
|
||||
} catch (IOException e) {
|
||||
logger.error("创建字节数组输入流失败", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return 文件输入流,失败返回null
|
||||
*/
|
||||
public InputStream downloadFile(String bucketName, String objectName) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
return minioClient.getObject(GetObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件下载失败: {}/{}", bucketName, objectName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return true 成功,false 失败
|
||||
*/
|
||||
public boolean deleteFile(String bucketName, String objectName) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
minioClient.removeObject(RemoveObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build());
|
||||
|
||||
logger.info("文件删除成功: {}/{}", bucketName, objectName);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("文件删除失败: {}/{}", bucketName, objectName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的预签名URL(用于临时访问)
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @param expiry 过期时间(秒)
|
||||
* @return 预签名URL,失败返回null
|
||||
*/
|
||||
public String getPresignedUrl(String bucketName, String objectName, int expiry) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.expiry(expiry, TimeUnit.SECONDS)
|
||||
.build());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("生成预签名URL失败: {}/{}", bucketName, objectName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return true 存在,false 不存在
|
||||
*/
|
||||
public boolean fileExists(String bucketName, String objectName) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
minioClient.statObject(StatObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build());
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName 对象名称
|
||||
* @return 文件信息,失败返回null
|
||||
*/
|
||||
public StatObjectResponse getFileInfo(String bucketName, String objectName) {
|
||||
try {
|
||||
MinioClient minioClient = minioConfig.getMinioClient();
|
||||
|
||||
return minioClient.statObject(StatObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("获取文件信息失败: {}/{}", bucketName, objectName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml
Normal file
119
urbanLifelineServ/file/src/main/resources/mapper/FileMapper.xml
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.xyzh.file.mapper.FileMapper">
|
||||
|
||||
<!-- 文件信息结果映射 -->
|
||||
<resultMap id="fileResultMap" type="org.xyzh.api.file.dto.TbSysFileDTO">
|
||||
<!-- 主键字段必须在最前面 -->
|
||||
<id column="file_id" property="fileId"/>
|
||||
|
||||
<!-- BaseDTO 字段 -->
|
||||
<result column="optsn" property="optsn"/>
|
||||
<result column="creator" property="creator"/>
|
||||
<result column="updater" property="updater"/>
|
||||
<result column="dept_path" property="deptPath"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="delete_time" property="deleteTime"/>
|
||||
<result column="deleted" property="deleted"/>
|
||||
|
||||
<!-- TbSysFileDTO 特有字段 -->
|
||||
<result column="name" property="name"/>
|
||||
<result column="path" property="path"/>
|
||||
<result column="size" property="size"/>
|
||||
<result column="type" property="type"/>
|
||||
<result column="storage_type" property="storageType"/>
|
||||
<result column="mime_type" property="mimeType"/>
|
||||
<result column="url" property="url"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="module" property="module"/>
|
||||
<result column="business_id" property="businessId"/>
|
||||
<result column="uploader" property="uploader"/>
|
||||
<result column="object_name" property="objectName"/>
|
||||
<result column="bucket_name" property="bucketName"/>
|
||||
<result column="md5_hash" property="md5Hash"/>
|
||||
<result column="extension" property="extension"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 插入文件记录 -->
|
||||
<insert id="insertFile" parameterType="org.xyzh.api.file.dto.TbSysFileDTO">
|
||||
INSERT INTO file.tb_sys_file (
|
||||
<!-- 必填字段(无默认值的NOT NULL字段) -->
|
||||
optsn, file_id, name, path, size
|
||||
<!-- 可选字段(包括有默认值的字段) -->
|
||||
<if test="creator != null">, creator</if>
|
||||
<if test="updater != null">, updater</if>
|
||||
<if test="deptPath != null">, dept_path</if>
|
||||
<if test="remark != null">, remark</if>
|
||||
<if test="createTime != null">, create_time</if>
|
||||
<if test="updateTime != null">, update_time</if>
|
||||
<if test="deleteTime != null">, delete_time</if>
|
||||
<if test="deleted != null">, deleted</if>
|
||||
<if test="type != null">, type</if>
|
||||
<if test="storageType != null">, storage_type</if>
|
||||
<if test="mimeType != null">, mime_type</if>
|
||||
<if test="url != null">, url</if>
|
||||
<if test="status != null">, status</if>
|
||||
<if test="module != null">, module</if>
|
||||
<if test="businessId != null">, business_id</if>
|
||||
<if test="uploader != null">, uploader</if>
|
||||
<if test="objectName != null">, object_name</if>
|
||||
<if test="bucketName != null">, bucket_name</if>
|
||||
<if test="md5Hash != null">, md5_hash</if>
|
||||
<if test="extension != null">, extension</if>
|
||||
) VALUES (
|
||||
<!-- 必填字段值 -->
|
||||
#{optsn}, #{fileId}, #{name}, #{path}, #{size}
|
||||
<!-- 可选字段值 -->
|
||||
<if test="creator != null">, #{creator}</if>
|
||||
<if test="updater != null">, #{updater}</if>
|
||||
<if test="deptPath != null">, #{deptPath}</if>
|
||||
<if test="remark != null">, #{remark}</if>
|
||||
<if test="createTime != null">, #{createTime}</if>
|
||||
<if test="updateTime != null">, #{updateTime}</if>
|
||||
<if test="deleteTime != null">, #{deleteTime}</if>
|
||||
<if test="deleted != null">, #{deleted}</if>
|
||||
<if test="type != null">, #{type}</if>
|
||||
<if test="storageType != null">, #{storageType}</if>
|
||||
<if test="mimeType != null">, #{mimeType}</if>
|
||||
<if test="url != null">, #{url}</if>
|
||||
<if test="status != null">, #{status}</if>
|
||||
<if test="module != null">, #{module}</if>
|
||||
<if test="businessId != null">, #{businessId}</if>
|
||||
<if test="uploader != null">, #{uploader}</if>
|
||||
<if test="objectName != null">, #{objectName}</if>
|
||||
<if test="bucketName != null">, #{bucketName}</if>
|
||||
<if test="md5Hash != null">, #{md5Hash}</if>
|
||||
<if test="extension != null">, #{extension}</if>
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 根据文件ID更新文件信息 -->
|
||||
<update id="updateByFileId" parameterType="org.xyzh.api.file.dto.TbSysFileDTO">
|
||||
UPDATE file.tb_sys_file
|
||||
<set>
|
||||
<if test="updater != null">updater = #{updater},</if>
|
||||
<if test="deptPath != null">dept_path = #{deptPath},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
update_time = CURRENT_TIMESTAMP,
|
||||
<if test="name != null">name = #{name},</if>
|
||||
<if test="path != null">path = #{path},</if>
|
||||
<if test="size != null">size = #{size},</if>
|
||||
<if test="type != null">type = #{type},</if>
|
||||
<if test="storageType != null">storage_type = #{storageType},</if>
|
||||
<if test="mimeType != null">mime_type = #{mimeType},</if>
|
||||
<if test="url != null">url = #{url},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="module != null">module = #{module},</if>
|
||||
<if test="businessId != null">business_id = #{businessId},</if>
|
||||
<if test="uploader != null">uploader = #{uploader},</if>
|
||||
<if test="objectName != null">object_name = #{objectName},</if>
|
||||
<if test="bucketName != null">bucket_name = #{bucketName},</if>
|
||||
<if test="md5Hash != null">md5_hash = #{md5Hash},</if>
|
||||
<if test="extension != null">extension = #{extension}</if>
|
||||
</set>
|
||||
WHERE file_id = #{fileId} AND ( deleted IS NULL OR deleted = false)
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user