feat(storage): 集成 MinIO 对象存储替代本地文件系统
- 添加 MinIO 配置类 MinioConfig 和服务类 MinioService - 在 CommonController 中实现 MinIO 上传下载功能 - 在 FileUploadController 中集成 MinIO 上传支持 - 在 SysProfileController 中添加头像上传到 MinIO 的逻辑 - 修改文件上传工具类支持 MinIO 上传 - 添加 MinIO 相关依赖到项目配置 - 实现本地文件系统与 MinIO 的条件切换机制
This commit is contained in:
@@ -13,12 +13,15 @@ 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;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
|
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||||
|
import com.ruoyi.common.utils.file.MinioService;
|
||||||
import com.ruoyi.framework.config.ServerConfig;
|
import com.ruoyi.framework.config.ServerConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,6 +38,12 @@ public class CommonController
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ServerConfig serverConfig;
|
private ServerConfig serverConfig;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioService minioService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioConfig minioConfig;
|
||||||
|
|
||||||
private static final String FILE_DELIMETER = ",";
|
private static final String FILE_DELIMETER = ",";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,14 +62,30 @@ public class CommonController
|
|||||||
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
|
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
|
||||||
}
|
}
|
||||||
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
|
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
|
||||||
String filePath = RuoYiConfig.getDownloadPath() + fileName;
|
|
||||||
|
|
||||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
if (isMinioEnabled())
|
||||||
FileUtils.setAttachmentResponseHeader(response, realFileName);
|
|
||||||
FileUtils.writeBytes(filePath, response.getOutputStream());
|
|
||||||
if (delete)
|
|
||||||
{
|
{
|
||||||
FileUtils.deleteFile(filePath);
|
// MinIO 下载
|
||||||
|
String objectName = "download/" + fileName;
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
FileUtils.setAttachmentResponseHeader(response, realFileName);
|
||||||
|
minioService.download(objectName, response.getOutputStream());
|
||||||
|
if (delete)
|
||||||
|
{
|
||||||
|
minioService.delete(objectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 本地文件系统下载
|
||||||
|
String filePath = RuoYiConfig.getDownloadPath() + fileName;
|
||||||
|
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
FileUtils.setAttachmentResponseHeader(response, realFileName);
|
||||||
|
FileUtils.writeBytes(filePath, response.getOutputStream());
|
||||||
|
if (delete)
|
||||||
|
{
|
||||||
|
FileUtils.deleteFile(filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -77,16 +102,28 @@ public class CommonController
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 上传文件路径
|
|
||||||
String filePath = RuoYiConfig.getUploadPath();
|
|
||||||
// 上传并返回新文件名称
|
|
||||||
String fileName = FileUploadUtils.upload(filePath, file);
|
|
||||||
String url = serverConfig.getUrl() + fileName;
|
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
ajax.put("url", url);
|
|
||||||
ajax.put("fileName", fileName);
|
|
||||||
ajax.put("newFileName", FileUtils.getName(fileName));
|
|
||||||
ajax.put("originalFilename", file.getOriginalFilename());
|
ajax.put("originalFilename", file.getOriginalFilename());
|
||||||
|
|
||||||
|
if (isMinioEnabled())
|
||||||
|
{
|
||||||
|
// MinIO 上传
|
||||||
|
String url = FileUploadUtils.uploadToMinio(minioService, "upload", file,
|
||||||
|
MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||||
|
ajax.put("url", url);
|
||||||
|
ajax.put("fileName", url);
|
||||||
|
ajax.put("newFileName", FileUtils.getName(file.getOriginalFilename()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 本地文件系统上传
|
||||||
|
String filePath = RuoYiConfig.getUploadPath();
|
||||||
|
String fileName = FileUploadUtils.upload(filePath, file);
|
||||||
|
String url = serverConfig.getUrl() + fileName;
|
||||||
|
ajax.put("url", url);
|
||||||
|
ajax.put("fileName", fileName);
|
||||||
|
ajax.put("newFileName", FileUtils.getName(fileName));
|
||||||
|
}
|
||||||
return ajax;
|
return ajax;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -103,22 +140,39 @@ public class CommonController
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 上传文件路径
|
|
||||||
String filePath = RuoYiConfig.getUploadPath();
|
|
||||||
List<String> urls = new ArrayList<String>();
|
List<String> urls = new ArrayList<String>();
|
||||||
List<String> fileNames = new ArrayList<String>();
|
List<String> fileNames = new ArrayList<String>();
|
||||||
List<String> newFileNames = new ArrayList<String>();
|
List<String> newFileNames = new ArrayList<String>();
|
||||||
List<String> originalFilenames = new ArrayList<String>();
|
List<String> originalFilenames = new ArrayList<String>();
|
||||||
for (MultipartFile file : files)
|
|
||||||
|
if (isMinioEnabled())
|
||||||
{
|
{
|
||||||
// 上传并返回新文件名称
|
// MinIO 上传
|
||||||
String fileName = FileUploadUtils.upload(filePath, file);
|
for (MultipartFile file : files)
|
||||||
String url = serverConfig.getUrl() + fileName;
|
{
|
||||||
urls.add(url);
|
String url = FileUploadUtils.uploadToMinio(minioService, "upload", file,
|
||||||
fileNames.add(fileName);
|
MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||||
newFileNames.add(FileUtils.getName(fileName));
|
urls.add(url);
|
||||||
originalFilenames.add(file.getOriginalFilename());
|
fileNames.add(url);
|
||||||
|
newFileNames.add(FileUtils.getName(file.getOriginalFilename()));
|
||||||
|
originalFilenames.add(file.getOriginalFilename());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 本地文件系统上传
|
||||||
|
String filePath = RuoYiConfig.getUploadPath();
|
||||||
|
for (MultipartFile file : files)
|
||||||
|
{
|
||||||
|
String fileName = FileUploadUtils.upload(filePath, file);
|
||||||
|
String url = serverConfig.getUrl() + fileName;
|
||||||
|
urls.add(url);
|
||||||
|
fileNames.add(fileName);
|
||||||
|
newFileNames.add(FileUtils.getName(fileName));
|
||||||
|
originalFilenames.add(file.getOriginalFilename());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
|
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
|
||||||
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
|
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
|
||||||
@@ -160,4 +214,12 @@ public class CommonController
|
|||||||
log.error("下载文件失败", e);
|
log.error("下载文件失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断 MinIO 是否启用
|
||||||
|
*/
|
||||||
|
private boolean isMinioEnabled()
|
||||||
|
{
|
||||||
|
return minioService != null && minioConfig != null && minioConfig.isEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
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;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
|
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||||
|
import com.ruoyi.common.utils.file.MinioService;
|
||||||
import com.ruoyi.framework.config.ServerConfig;
|
import com.ruoyi.framework.config.ServerConfig;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -25,26 +28,53 @@ public class FileUploadController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ServerConfig serverConfig;
|
private ServerConfig serverConfig;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioService minioService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioConfig minioConfig;
|
||||||
|
|
||||||
@Anonymous
|
@Anonymous
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
public AjaxResult uploadFile(MultipartFile file) {
|
public AjaxResult uploadFile(MultipartFile file) {
|
||||||
try {
|
try {
|
||||||
log.info("文件 {} 上传中...", file.getOriginalFilename());
|
log.info("文件 {} 上传中...", file.getOriginalFilename());
|
||||||
// 上传文件路径
|
|
||||||
String filePath = RuoYiConfig.getUploadPath();
|
|
||||||
// 上传并返回新文件名称
|
|
||||||
String fileName = FileUploadUtils.upload(filePath, file);
|
|
||||||
String url = serverConfig.getUrl() + fileName;
|
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
ajax.put("url", url);
|
|
||||||
ajax.put("fileName", fileName);
|
|
||||||
ajax.put("newFileName", FileUtils.getName(fileName));
|
|
||||||
ajax.put("originalFilename", file.getOriginalFilename());
|
ajax.put("originalFilename", file.getOriginalFilename());
|
||||||
|
|
||||||
|
if (isMinioEnabled())
|
||||||
|
{
|
||||||
|
// MinIO 上传
|
||||||
|
String url = FileUploadUtils.uploadToMinio(minioService, "upload", file,
|
||||||
|
MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||||
|
ajax.put("url", url);
|
||||||
|
ajax.put("fileName", url);
|
||||||
|
ajax.put("newFileName", FileUtils.getName(file.getOriginalFilename()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 本地文件系统上传
|
||||||
|
String filePath = RuoYiConfig.getUploadPath();
|
||||||
|
String fileName = FileUploadUtils.upload(filePath, file);
|
||||||
|
String url = serverConfig.getUrl() + fileName;
|
||||||
|
ajax.put("url", url);
|
||||||
|
ajax.put("fileName", fileName);
|
||||||
|
ajax.put("newFileName", FileUtils.getName(fileName));
|
||||||
|
}
|
||||||
|
|
||||||
log.info("文件 {} 上传成功!", file.getOriginalFilename());
|
log.info("文件 {} 上传成功!", file.getOriginalFilename());
|
||||||
return ajax;
|
return ajax;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new NonCaptureException(StringUtils.format("文件 {} 上传失败!", file.getOriginalFilename()), e);
|
throw new NonCaptureException(StringUtils.format("文件 {} 上传失败!", file.getOriginalFilename()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 判断 MinIO 是否启用
|
||||||
|
*/
|
||||||
|
private boolean isMinioEnabled()
|
||||||
|
{
|
||||||
|
return minioService != null && minioConfig != null && minioConfig.isEnabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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;
|
||||||
@@ -21,6 +22,7 @@ import com.ruoyi.common.utils.SecurityUtils;
|
|||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||||
|
import com.ruoyi.common.utils.file.MinioService;
|
||||||
import com.ruoyi.framework.web.service.TokenService;
|
import com.ruoyi.framework.web.service.TokenService;
|
||||||
import com.ruoyi.system.service.ISysUserService;
|
import com.ruoyi.system.service.ISysUserService;
|
||||||
|
|
||||||
@@ -39,6 +41,12 @@ public class SysProfileController extends BaseController
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private TokenService tokenService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioService minioService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MinioConfig minioConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 个人信息
|
* 个人信息
|
||||||
*/
|
*/
|
||||||
@@ -124,7 +132,15 @@ public class SysProfileController extends BaseController
|
|||||||
if (!file.isEmpty())
|
if (!file.isEmpty())
|
||||||
{
|
{
|
||||||
LoginUser loginUser = getLoginUser();
|
LoginUser loginUser = getLoginUser();
|
||||||
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
String avatar;
|
||||||
|
if (isMinioEnabled())
|
||||||
|
{
|
||||||
|
avatar = FileUploadUtils.uploadToMinio(minioService, "avatar", file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||||
|
}
|
||||||
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
|
if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
|
||||||
{
|
{
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
@@ -137,4 +153,12 @@ public class SysProfileController extends BaseController
|
|||||||
}
|
}
|
||||||
return error("上传图片异常,请联系管理员");
|
return error("上传图片异常,请联系管理员");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断 MinIO 是否启用
|
||||||
|
*/
|
||||||
|
private boolean isMinioEnabled()
|
||||||
|
{
|
||||||
|
return minioService != null && minioConfig != null && minioConfig.isEnabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,19 @@ ruoyi:
|
|||||||
# 验证码类型 math 数字计算 char 字符验证
|
# 验证码类型 math 数字计算 char 字符验证
|
||||||
captchaType: math
|
captchaType: math
|
||||||
|
|
||||||
|
# MinIO 对象存储配置(开启后将文件存储到 MinIO 而非本地文件系统)
|
||||||
|
minio:
|
||||||
|
# 是否启用(false 则使用本地文件系统)
|
||||||
|
enabled: false
|
||||||
|
# MinIO 服务地址
|
||||||
|
endpoint: http://140.143.206.120:10990
|
||||||
|
# 访问密钥
|
||||||
|
accessKey: adminMinio
|
||||||
|
# 秘密密钥
|
||||||
|
secretKey: adminMinio
|
||||||
|
# 存储桶名称
|
||||||
|
bucketName: double-rack
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
# 服务器的HTTP端口,默认为8080
|
||||||
|
|||||||
@@ -113,6 +113,13 @@
|
|||||||
<artifactId>UserAgentUtils</artifactId>
|
<artifactId>UserAgentUtils</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MinIO 对象存储 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.minio</groupId>
|
||||||
|
<artifactId>minio</artifactId>
|
||||||
|
<version>8.5.7</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- servlet包 -->
|
<!-- servlet包 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.ruoyi.common.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinIO 对象存储配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "minio")
|
||||||
|
public class MinioConfig
|
||||||
|
{
|
||||||
|
/** 是否启用 MinIO(false 则使用本地文件系统) */
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
/** MinIO 服务地址 */
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
/** 访问密钥 */
|
||||||
|
private String accessKey;
|
||||||
|
|
||||||
|
/** 秘密密钥 */
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
/** 存储桶名称 */
|
||||||
|
private String bucketName;
|
||||||
|
|
||||||
|
public boolean isEnabled()
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndpoint()
|
||||||
|
{
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndpoint(String endpoint)
|
||||||
|
{
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessKey()
|
||||||
|
{
|
||||||
|
return accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessKey(String accessKey)
|
||||||
|
{
|
||||||
|
this.accessKey = accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey()
|
||||||
|
{
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecretKey(String secretKey)
|
||||||
|
{
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucketName()
|
||||||
|
{
|
||||||
|
return bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBucketName(String bucketName)
|
||||||
|
{
|
||||||
|
this.bucketName = bucketName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -214,6 +214,36 @@ public class FileUploadUtils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到 MinIO(使用与本地文件系统相同的文件名规则)
|
||||||
|
*
|
||||||
|
* @param minioService MinIO 服务实例
|
||||||
|
* @param baseDir 基础目录(如 upload、avatar)
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param allowedExtension 允许的文件扩展名
|
||||||
|
* @return MinIO 文件访问 URL
|
||||||
|
* @throws IOException 上传失败
|
||||||
|
*/
|
||||||
|
public static final String uploadToMinio(MinioService minioService, String baseDir, MultipartFile file,
|
||||||
|
String[] allowedExtension)
|
||||||
|
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
|
||||||
|
InvalidExtensionException
|
||||||
|
{
|
||||||
|
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
|
||||||
|
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
|
||||||
|
{
|
||||||
|
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertAllowed(file, allowedExtension);
|
||||||
|
|
||||||
|
// 生成 objectName: upload/2025/04/13/originalName_uuid.ext
|
||||||
|
String fileName = extractFilename(file);
|
||||||
|
String objectName = baseDir + "/" + fileName;
|
||||||
|
|
||||||
|
return minioService.upload(file, objectName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件名的后缀
|
* 获取文件名的后缀
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package com.ruoyi.common.utils.file;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import io.minio.BucketExistsArgs;
|
||||||
|
import io.minio.GetObjectArgs;
|
||||||
|
import io.minio.MakeBucketArgs;
|
||||||
|
import io.minio.MinioClient;
|
||||||
|
import io.minio.PutObjectArgs;
|
||||||
|
import io.minio.RemoveObjectArgs;
|
||||||
|
import io.minio.StatObjectArgs;
|
||||||
|
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.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.ruoyi.common.config.MinioConfig;
|
||||||
|
|
||||||
|
import io.minio.http.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinIO 对象存储服务
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@ConditionalOnProperty(prefix = "minio", name = "enabled", havingValue = "true")
|
||||||
|
public class MinioService
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MinioService.class);
|
||||||
|
|
||||||
|
private MinioClient minioClient;
|
||||||
|
|
||||||
|
private MinioConfig minioConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MinioService(MinioConfig minioConfig)
|
||||||
|
{
|
||||||
|
this.minioConfig = minioConfig;
|
||||||
|
this.minioClient = MinioClient.builder()
|
||||||
|
.endpoint(minioConfig.getEndpoint())
|
||||||
|
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
|
||||||
|
.build();
|
||||||
|
ensureBucketExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保存储桶存在,不存在则自动创建
|
||||||
|
*/
|
||||||
|
private void ensureBucketExists()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String bucketName = minioConfig.getBucketName();
|
||||||
|
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
|
||||||
|
log.info("MinIO 存储桶 [{}] 已自动创建", bucketName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MinIO 存储桶初始化失败", e);
|
||||||
|
throw new RuntimeException("MinIO 存储桶初始化失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到 MinIO
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param objectName 对象名称(含路径,如 upload/2025/04/13/xxx.png)
|
||||||
|
* @return 文件访问 URL
|
||||||
|
*/
|
||||||
|
public String upload(MultipartFile file, String objectName) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream";
|
||||||
|
minioClient.putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.stream(file.getInputStream(), file.getSize(), -1)
|
||||||
|
.contentType(contentType)
|
||||||
|
.build());
|
||||||
|
log.info("MinIO 上传成功: {} -> bucket:{}", objectName, minioConfig.getBucketName());
|
||||||
|
return getUrl(objectName);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MinIO 上传失败: {}", objectName, e);
|
||||||
|
throw new IOException("MinIO 上传失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传输入流到 MinIO
|
||||||
|
*
|
||||||
|
* @param stream 输入流
|
||||||
|
* @param objectName 对象名称
|
||||||
|
* @param size 数据大小
|
||||||
|
* @param contentType 内容类型
|
||||||
|
* @return 文件访问 URL
|
||||||
|
*/
|
||||||
|
public String upload(InputStream stream, String objectName, long size, String contentType) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String ct = (contentType != null) ? contentType : "application/octet-stream";
|
||||||
|
minioClient.putObject(
|
||||||
|
PutObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.stream(stream, size, -1)
|
||||||
|
.contentType(ct)
|
||||||
|
.build());
|
||||||
|
return getUrl(objectName);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MinIO 上传失败: {}", objectName, e);
|
||||||
|
throw new IOException("MinIO 上传失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件访问 URL(7天有效期)
|
||||||
|
*
|
||||||
|
* @param objectName 对象名称
|
||||||
|
* @return 预签名 URL
|
||||||
|
*/
|
||||||
|
public String getUrl(String objectName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return minioClient.getPresignedObjectUrl(
|
||||||
|
io.minio.GetPresignedObjectUrlArgs.builder()
|
||||||
|
.method(Method.GET)
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.expiry(7 * 24 * 60 * 60) // 7天有效期
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.warn("MinIO 获取 URL 失败: {}", objectName, e);
|
||||||
|
// 返回拼接直连地址
|
||||||
|
return minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + objectName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件到输出流
|
||||||
|
*
|
||||||
|
* @param objectName 对象名称
|
||||||
|
* @param os 输出流
|
||||||
|
*/
|
||||||
|
public void download(String objectName, OutputStream os) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try (InputStream is = minioClient.getObject(
|
||||||
|
GetObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.build()))
|
||||||
|
{
|
||||||
|
byte[] buf = new byte[8192];
|
||||||
|
int len;
|
||||||
|
while ((len = is.read(buf)) > -1)
|
||||||
|
{
|
||||||
|
os.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MinIO 下载失败: {}", objectName, e);
|
||||||
|
throw new IOException("MinIO 下载失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*
|
||||||
|
* @param objectName 对象名称
|
||||||
|
*/
|
||||||
|
public void delete(String objectName) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
minioClient.removeObject(
|
||||||
|
RemoveObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.build());
|
||||||
|
log.info("MinIO 删除成功: {}", objectName);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MinIO 删除失败: {}", objectName, e);
|
||||||
|
throw new IOException("MinIO 删除失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断对象是否存在
|
||||||
|
*
|
||||||
|
* @param objectName 对象名称
|
||||||
|
* @return true 存在
|
||||||
|
*/
|
||||||
|
public boolean exists(String objectName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
minioClient.statObject(
|
||||||
|
StatObjectArgs.builder()
|
||||||
|
.bucket(minioConfig.getBucketName())
|
||||||
|
.object(objectName)
|
||||||
|
.build());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (ErrorResponseException e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.warn("MinIO 检查对象存在性失败: {}", objectName, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 MinIO 配置
|
||||||
|
*/
|
||||||
|
public MinioConfig getMinioConfig()
|
||||||
|
{
|
||||||
|
return minioConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user