fix(file): 修复MinIO服务初始化和端口配置问题

- 修正MinIO端点端口号从10990改为10900
- 移除对MinioConfig的直接依赖注入,改用minioService.isInitialized()判断
- 重构MinioService实现延迟初始化和异常处理机制
- 添加初始化状态检查方法避免未初始化时的操作
- 更新依赖配置确保OkHttp和okio版本兼容性
This commit is contained in:
2026-06-09 13:33:12 +08:00
parent 78e2c3023b
commit ab2f4a611a
7 changed files with 80 additions and 45 deletions

View File

@@ -73,6 +73,19 @@
<artifactId>ruoyi-cost</artifactId> <artifactId>ruoyi-cost</artifactId>
</dependency> </dependency>
<!-- MinIO 所需的 OkHttp 4.x覆盖其他依赖传递的 3.x -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- OkHttp 4.x 所需的 okio 3.x -->
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.MinioConfig;
import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
@@ -41,9 +40,6 @@ public class CommonController
@Autowired(required = false) @Autowired(required = false)
private MinioService minioService; private MinioService minioService;
@Autowired(required = false)
private MinioConfig minioConfig;
private static final String FILE_DELIMETER = ","; private static final String FILE_DELIMETER = ",";
/** /**
@@ -220,6 +216,6 @@ public class CommonController
*/ */
private boolean isMinioEnabled() private boolean isMinioEnabled()
{ {
return minioService != null && minioConfig != null && minioConfig.isEnabled(); return minioService != null && minioService.isInitialized();
} }
} }

View File

@@ -1,7 +1,6 @@
package com.ruoyi.web.controller.common; package com.ruoyi.web.controller.common;
import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.config.MinioConfig;
import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.NonCaptureException; import com.ruoyi.common.exception.NonCaptureException;
@@ -31,9 +30,6 @@ public class FileUploadController {
@Autowired(required = false) @Autowired(required = false)
private MinioService minioService; private MinioService minioService;
@Autowired(required = false)
private MinioConfig minioConfig;
@Anonymous @Anonymous
@PostMapping("/upload") @PostMapping("/upload")
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
@@ -75,6 +71,6 @@ public class FileUploadController {
*/ */
private boolean isMinioEnabled() private boolean isMinioEnabled()
{ {
return minioService != null && minioConfig != null && minioConfig.isEnabled(); return minioService != null && minioService.isInitialized();
} }
} }

View File

@@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.MinioConfig;
import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
@@ -44,9 +43,6 @@ public class SysProfileController extends BaseController
@Autowired(required = false) @Autowired(required = false)
private MinioService minioService; private MinioService minioService;
@Autowired(required = false)
private MinioConfig minioConfig;
/** /**
* 个人信息 * 个人信息
*/ */
@@ -159,6 +155,6 @@ public class SysProfileController extends BaseController
*/ */
private boolean isMinioEnabled() private boolean isMinioEnabled()
{ {
return minioService != null && minioConfig != null && minioConfig.isEnabled(); return minioService != null && minioService.isInitialized();
} }
} }

View File

@@ -18,7 +18,7 @@ minio:
# 是否启用false 则使用本地文件系统) # 是否启用false 则使用本地文件系统)
enabled: true enabled: true
# MinIO 服务地址 # MinIO 服务地址
endpoint: http://140.143.206.120:10990 endpoint: http://140.143.206.120:10900
# 访问密钥 # 访问密钥
accessKey: m4X4SsEOhEfTTiv7wI8X accessKey: m4X4SsEOhEfTTiv7wI8X
# 秘密密钥 # 秘密密钥

View File

@@ -125,6 +125,12 @@
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>
<version>4.12.0</version> <version>4.12.0</version>
</dependency> </dependency>
<!-- OkHttp 4.x 所需的 okio 3.x -->
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>3.6.0</version>
</dependency>
<!-- servlet包 --> <!-- servlet包 -->
<dependency> <dependency>

View File

@@ -12,16 +12,10 @@ import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs; import io.minio.RemoveObjectArgs;
import io.minio.StatObjectArgs; import io.minio.StatObjectArgs;
import io.minio.errors.ErrorResponseException; import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -35,47 +29,65 @@ import io.minio.http.Method;
* @author ruoyi * @author ruoyi
*/ */
@Service @Service
@ConditionalOnProperty(prefix = "minio", name = "enabled", havingValue = "true")
public class MinioService public class MinioService
{ {
private static final Logger log = LoggerFactory.getLogger(MinioService.class); private static final Logger log = LoggerFactory.getLogger(MinioService.class);
private MinioClient minioClient; private MinioClient minioClient;
private MinioConfig minioConfig; private MinioConfig minioConfig;
private volatile boolean initialized = false;
@Autowired @Autowired
public MinioService(MinioConfig minioConfig) public MinioService(MinioConfig minioConfig)
{ {
this.minioConfig = minioConfig; this.minioConfig = minioConfig;
if (!minioConfig.isEnabled())
{
log.info("MinIO 未启用minio.enabled=false跳过初始化");
return;
}
try
{
this.minioClient = MinioClient.builder() this.minioClient = MinioClient.builder()
.endpoint(minioConfig.getEndpoint()) .endpoint(minioConfig.getEndpoint())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()) .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build(); .build();
ensureBucketExists(); ensureBucketExists();
this.initialized = true;
log.info("MinIO 客户端初始化成功: endpoint={}, bucket={}",
minioConfig.getEndpoint(), minioConfig.getBucketName());
}
catch (Exception e)
{
log.warn("MinIO 初始化失败(应用将继续运行,上传降级为本地文件系统): {}", e.getMessage());
this.minioClient = null;
this.initialized = false;
}
}
/**
* 检查 MinIO 客户端是否已正确初始化
*/
private void checkInitialized()
{
if (!initialized || minioClient == null)
{
throw new IllegalStateException("MinIO 未启用或初始化失败,请检查配置");
}
} }
/** /**
* 确保存储桶存在,不存在则自动创建 * 确保存储桶存在,不存在则自动创建
*/ */
private void ensureBucketExists() private void ensureBucketExists() throws Exception
{
try
{ {
String bucketName = minioConfig.getBucketName(); String bucketName = minioConfig.getBucketName();
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()))
if (!exists)
{ {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("MinIO 存储桶 [{}] 已自动创建", bucketName); log.info("MinIO 存储桶 [{}] 已自动创建", bucketName);
} }
} }
catch (Exception e)
{
log.error("MinIO 存储桶初始化失败", e);
throw new RuntimeException("MinIO 存储桶初始化失败: " + e.getMessage(), e);
}
}
/** /**
* 上传文件到 MinIO * 上传文件到 MinIO
@@ -86,6 +98,7 @@ public class MinioService
*/ */
public String upload(MultipartFile file, String objectName) throws IOException public String upload(MultipartFile file, String objectName) throws IOException
{ {
checkInitialized();
try try
{ {
String contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream"; String contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream";
@@ -117,6 +130,7 @@ public class MinioService
*/ */
public String upload(InputStream stream, String objectName, long size, String contentType) throws IOException public String upload(InputStream stream, String objectName, long size, String contentType) throws IOException
{ {
checkInitialized();
try try
{ {
String ct = (contentType != null) ? contentType : "application/octet-stream"; String ct = (contentType != null) ? contentType : "application/octet-stream";
@@ -144,6 +158,7 @@ public class MinioService
*/ */
public String getUrl(String objectName) public String getUrl(String objectName)
{ {
checkInitialized();
try try
{ {
return minioClient.getPresignedObjectUrl( return minioClient.getPresignedObjectUrl(
@@ -151,13 +166,12 @@ public class MinioService
.method(Method.GET) .method(Method.GET)
.bucket(minioConfig.getBucketName()) .bucket(minioConfig.getBucketName())
.object(objectName) .object(objectName)
.expiry(7 * 24 * 60 * 60) // 7天有效期 .expiry(7 * 24 * 60 * 60)
.build()); .build());
} }
catch (Exception e) catch (Exception e)
{ {
log.warn("MinIO 获取 URL 失败: {}", objectName, e); log.warn("MinIO 获取 URL 失败: {}", objectName, e);
// 返回拼接直连地址
return minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + objectName; return minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + objectName;
} }
} }
@@ -170,6 +184,7 @@ public class MinioService
*/ */
public void download(String objectName, OutputStream os) throws IOException public void download(String objectName, OutputStream os) throws IOException
{ {
checkInitialized();
try try
{ {
try (InputStream is = minioClient.getObject( try (InputStream is = minioClient.getObject(
@@ -200,6 +215,7 @@ public class MinioService
*/ */
public void delete(String objectName) throws IOException public void delete(String objectName) throws IOException
{ {
checkInitialized();
try try
{ {
minioClient.removeObject( minioClient.removeObject(
@@ -224,6 +240,10 @@ public class MinioService
*/ */
public boolean exists(String objectName) public boolean exists(String objectName)
{ {
if (!initialized || minioClient == null)
{
return false;
}
try try
{ {
minioClient.statObject( minioClient.statObject(
@@ -244,6 +264,14 @@ public class MinioService
} }
} }
/**
* 判断 MinIO 是否已正确初始化并可用
*/
public boolean isInitialized()
{
return initialized && minioClient != null;
}
/** /**
* 获取 MinIO 配置 * 获取 MinIO 配置
*/ */