diff --git a/ruoyi-admin/src/main/resources/application-oss.yml b/ruoyi-admin/src/main/resources/application-oss.yml
new file mode 100644
index 0000000000000000000000000000000000000000..579c36bf39a60a3cf02cee9323cdcc5cd3d3f704
--- /dev/null
+++ b/ruoyi-admin/src/main/resources/application-oss.yml
@@ -0,0 +1,10 @@
+# oss配置
+oss:
+ enable: false
+ primary: MASTER
+ client:
+ MASTER:
+ accessKeyId:
+ accessKeySecret:
+ bucketName:
+ endpoint:
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 45c0a296f102131e1188eca359c9cbc2ad7a09c2..07bd6a3c014d6760dbfde12c8c469599f6c06422 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -12,7 +12,7 @@ ruoyi:
addressEnabled: false
# 验证码类型 math 数组计算 char 字符验证
captchaType: math
- # 指定默认文件服务类型(值为disk代表使用磁盘作为文件操作服务,minio代表使用minio作为文件操作服务)
+ # 指定默认文件服务类型(值为disk代表使用磁盘作为文件操作服务,minio代表使用minio作为文件操作服务,oss代表使用oss作为文件操作服务)
fileServer: disk
# 开发环境配置
@@ -69,7 +69,7 @@ spring:
# 国际化资源文件路径
basename: i18n/messages
profiles:
- active: druid,mybatis,auth,pay,middleware,lp
+ active: druid,mybatis,auth,pay,middleware,lp,oss
# 文件上传
servlet:
multipart:
diff --git a/ruoyi-plugins/pom.xml b/ruoyi-plugins/pom.xml
index 9807d9896eed0fc03e76740061e9bf22db015951..80a91f1f1b57791529e59f04d02e71af513e359f 100644
--- a/ruoyi-plugins/pom.xml
+++ b/ruoyi-plugins/pom.xml
@@ -14,6 +14,7 @@
3.10.8
3.5.8
4.1.112.Final
+ 3.18.1
@@ -87,6 +88,20 @@
${netty.version}
+
+
+ com.ruoyi
+ ruoyi-alibaba-oss
+ ${ruoyi.version}
+
+
+
+
+ com.aliyun.oss
+ aliyun-sdk-oss
+ ${oss.version}
+
+
@@ -99,6 +114,7 @@
ruoyi-plugins-starter
ruoyi-mybatis-interceptor
ruoyi-netty
+ ruoyi-alibaba-oss
pom
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/pom.xml b/ruoyi-plugins/ruoyi-alibaba-oss/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0871d9b7951ef331f7de04c6017e5d152ef814a1
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+ ruoyi-plugins
+ com.ruoyi
+ 3.8.8.3.1
+
+ com.ruoyi
+ ruoyi-alibaba-oss
+ oss中间件
+
+
+
+
+ com.ruoyi
+ ruoyi-common
+
+
+
+
+ com.aliyun.oss
+ aliyun-sdk-oss
+ 3.10.2
+
+
+
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssConfig.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..c814cb7ae5b632ba2e0ba95cf7eb8963e757737b
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssConfig.java
@@ -0,0 +1,135 @@
+package com.ruoyi.alibaba.oss.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import com.aliyun.oss.ClientException;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.OSSException;
+import com.ruoyi.alibaba.oss.domain.AliOssBucket;
+
+@Configuration("AliOssConfiguration")
+@ConditionalOnProperty(prefix = "oss", name = "enable", havingValue = "true", matchIfMissing = false)
+@ConfigurationProperties(prefix = "oss")
+public class AliOssConfig implements InitializingBean {
+ private static final Logger logger = LoggerFactory.getLogger(AliOssConfig.class);
+ public static int maxSize;
+ private String prefix = "/oss"; // 根据需要调整前缀
+ private Map client = new HashMap<>();
+ private String primary;
+ private Map targetAliOssBucket = new HashMap<>();
+ private AliOssBucket masterBucket;
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ public AliOssBucket getMasterBucket() {
+ return this.masterBucket;
+ }
+
+ public AliOssBucket getBucket(String client) {
+ return targetAliOssBucket.get(client);
+ }
+
+ public Map getClient() {
+ return client;
+ }
+
+ public void setClient(Map client) {
+ this.client = client;
+ }
+
+ public String getPrimary() {
+ return primary;
+ }
+
+ public void setPrimary(String primary) {
+ this.primary = primary;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (client == null || client.isEmpty()) {
+ throw new RuntimeException("Client properties cannot be null or empty");
+ }
+
+ client.forEach((name, props) -> {
+ try {
+ AliOssBucket aliOssBucket = createOssClient(name, props);
+ targetAliOssBucket.put(name, aliOssBucket);
+ } catch (Exception e) {
+ logger.error("Failed to create OSS client for {}: {}", name, e.getMessage(), e);
+ }
+ });
+
+ if (targetAliOssBucket.get(primary) == null) {
+ throw new RuntimeException("Primary client " + primary + " does not exist");
+ }
+ masterBucket = targetAliOssBucket.get(primary);
+ }
+
+ private AliOssBucket createOssClient(String name, AliOssProperties props) {
+ if (props == null || props.getEndpoint() == null || props.getAccessKeyId() == null ||
+ props.getAccessKeySecret() == null || props.getBucketName() == null) {
+ throw new IllegalArgumentException("AliOssProperties or its required fields cannot be null");
+ }
+
+ OSS client = new OSSClientBuilder().build(props.getEndpoint(), props.getAccessKeyId(), props.getAccessKeySecret());
+ AliOssBucket ossBucket = new AliOssBucket(client, props.getBucketName());
+ validateOssBucket(ossBucket);
+ logger.info("数据桶:{} - 链接成功", name);
+ return ossBucket;
+ }
+
+ private static void validateOssBucket(AliOssBucket aliOssBucket) {
+ OSS ossClient = aliOssBucket.getOssClient();
+ String bucketName = aliOssBucket.getBucketName();
+ try {
+ if (!ossClient.doesBucketExist(bucketName)) {
+ throw new RuntimeException("Bucket " + bucketName + " does not exist");
+ }
+ } catch (OSSException oe) {
+ logger.error("OSSException: " + oe.getMessage(), oe);
+ throw new RuntimeException("OSS error: " + oe.getMessage());
+ } catch (ClientException ce) {
+ logger.error("ClientException: " + ce.getMessage(), ce);
+ throw new RuntimeException("Client error: " + ce.getMessage());
+ } catch (Exception e) {
+ logger.error("Exception: " + e.getMessage(), e);
+ throw new RuntimeException("Error validating OSS bucket: " + e.getMessage());
+ }
+ }
+
+
+ public String getMasterBucketName() {
+ if (masterBucket != null) {
+ return masterBucket.getBucketName();
+ }
+ return null; // 或者抛出异常,根据业务需求决定
+ }
+
+ public String getBucketName(String client) {
+ if (client != null && targetAliOssBucket.containsKey(client)) {
+ return targetAliOssBucket.get(client).getBucketName();
+ }
+ return null; // 或者抛出异常,根据业务需求决定
+ }
+
+}
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssProperties.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssProperties.java
new file mode 100644
index 0000000000000000000000000000000000000000..f20341a512ed6e6b18b640adc26056ff518b3396
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/config/AliOssProperties.java
@@ -0,0 +1,62 @@
+package com.ruoyi.alibaba.oss.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import com.aliyun.oss.OSSClient;
+
+@ConfigurationProperties(prefix = "oss.client")
+public class AliOssProperties {
+
+ private String endpoint;
+ private String accessKeyId;
+ private String accessKeySecret;
+ private String bucketName;
+
+ private OSSClient ossClient;
+
+ public AliOssProperties(){
+
+ }
+
+ public AliOssProperties(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
+ this.endpoint = endpoint;
+ this.accessKeyId = accessKeyId;
+ this.accessKeySecret = accessKeySecret;
+ this.bucketName = bucketName;
+ }
+ public String getEndpoint() {
+ return endpoint;
+ }
+ public void setEndpoint(String endpoint) {
+ this.endpoint = endpoint;
+ }
+ public String getAccessKeyId() {
+ return accessKeyId;
+ }
+ public void setAccessKeyId(String accessKeyId) {
+ this.accessKeyId = accessKeyId;
+ }
+ public String getAccessKeySecret() {
+ return accessKeySecret;
+ }
+ public void setAccessKeySecret(String accessKeySecret) {
+ this.accessKeySecret = accessKeySecret;
+ }
+ public String getBucketName() {
+ return bucketName;
+ }
+ public void setBucketName(String bucketName) {
+ this.bucketName = bucketName;
+ }
+
+ public OSSClient getOssClient() {
+ return ossClient;
+ }
+
+ public void setOssClient(OSSClient ossClient) {
+ this.ossClient = ossClient;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/controller/AliOssController.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/controller/AliOssController.java
new file mode 100644
index 0000000000000000000000000000000000000000..598f864080d80e6a5933d9cd5e532595b0854339
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/controller/AliOssController.java
@@ -0,0 +1,68 @@
+package com.ruoyi.alibaba.oss.controller;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.ruoyi.alibaba.oss.domain.AliOssFileVO;
+import com.ruoyi.alibaba.oss.utils.AliOssUtil;
+
+import io.swagger.v3.oas.annotations.Operation;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@RestController
+@RequestMapping("/oss")
+public class AliOssController {
+
+ @Autowired
+ private AliOssUtil aliOssUtil;
+
+ @Operation(summary = "下载接口oss")
+ @GetMapping("/{client}")
+ public void downLoadFile(HttpServletRequest request, HttpServletResponse response,@PathVariable("client") String client,
+ @RequestParam("fileName") String fileName) throws Exception {
+ AliOssFileVO file = aliOssUtil.getFile(client, fileName);
+ // 设置响应头
+ String contentType = file.getHeaders().getOrDefault("Content-Type", "application/octet-stream");
+ response.setContentType(contentType);
+
+ // 设置内容长度
+ String contentLength = file.getHeaders().get("Content-Length");
+ if (contentLength != null) {
+ response.setContentLengthLong(Long.parseLong(contentLength));
+ }
+ response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
+ // 写入文件内容到响应流
+ try (InputStream inputStream = file.getFileInputSteam();
+ OutputStream outputStream = response.getOutputStream()) {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ }
+ outputStream.flush();
+ } catch (IOException e) {
+ throw new IOException("Error writing file to output stream", e);
+ }
+ }
+
+
+ //上传接口
+ @Operation(summary = "上传接口oss")
+ @PutMapping("/{client}")
+ public String uploadFile(HttpServletRequest request, HttpServletResponse response,
+ @PathVariable("client") String client, @RequestParam("file") MultipartFile file) throws Exception {
+ return AliOssUtil.uploadFile(client, file);
+ }
+}
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssBucket.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssBucket.java
new file mode 100644
index 0000000000000000000000000000000000000000..3658edbe7f4fc9855d03a21349ce8ad5365379cd
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssBucket.java
@@ -0,0 +1,150 @@
+package com.ruoyi.alibaba.oss.domain;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.DeleteObjectsRequest;
+import com.aliyun.oss.model.GetObjectRequest;
+import com.aliyun.oss.model.OSSObject;
+import com.aliyun.oss.model.ObjectMetadata;
+import com.aliyun.oss.model.PutObjectRequest;
+import com.ruoyi.alibaba.oss.exception.AliOssClientErrorException;
+
+public class AliOssBucket {
+
+ private static final Logger logger = LoggerFactory.getLogger(AliOssBucket.class);
+
+ private String bucketName;
+ private OSS ossClient;
+
+ public AliOssBucket() {
+ }
+
+ // 构造函数
+ public AliOssBucket(OSS ossClient, String bucketName) {
+ this.ossClient = ossClient;
+ this.bucketName = bucketName;
+ }
+
+ public OSS getOssClient() {
+ return ossClient;
+ }
+
+ public void setOssClient(OSS ossClient) {
+ this.ossClient = ossClient;
+ }
+
+ public String getBucketName() {
+ return bucketName;
+ }
+
+ public void setBucketName(String bucketName) {
+ this.bucketName = bucketName;
+ }
+
+ public void put(String fileName, MultipartFile file) throws Exception {
+ put(fileName, file.getContentType(), file.getInputStream());
+ }
+
+ // 上传文件
+ public void put(String filePath, String contentType, InputStream inputStream) throws Exception {
+ try {
+ // 创建 ObjectMetadata 对象,并设置内容类型
+ ObjectMetadata metadata = new ObjectMetadata();
+ metadata.setContentType(contentType);
+ metadata.setContentLength(inputStream.available()); // 使用 InputStream 的 available 方法
+
+ // 创建 PutObjectRequest 对象
+ PutObjectRequest putRequest = new PutObjectRequest(bucketName, filePath, inputStream, metadata);
+
+ // 上传对象
+ ossClient.putObject(putRequest);
+ } catch (Exception e) {
+ logger.error("Error uploading file: {}", e.getMessage(), e);
+ throw new AliOssClientErrorException("Error uploading file: " + e.getMessage(), e);
+ }
+ }
+
+ public void put(PutObjectRequest putObjectRequest) throws Exception {
+ try {
+ this.ossClient.putObject(putObjectRequest);
+ } catch (Exception e) {
+ logger.error("Error uploading file: {}", e.getMessage(), e);
+ throw new AliOssClientErrorException("Error uploading file: " + e.getMessage(), e);
+ }
+ }
+
+ public void remove(String filePath) throws Exception {
+ // 删除单个对象
+ ossClient.deleteObject(bucketName, filePath);
+ }
+
+ public void removeMultiple(List filePaths) throws Exception {
+ // 删除多个对象
+ try {
+ DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(bucketName).withKeys(filePaths);
+ ossClient.deleteObjects(deleteRequest);
+ } catch (Exception e) {
+ logger.error("Error deleting files: {}", e.getMessage(), e);
+ throw new AliOssClientErrorException("Error deleting files: " + e.getMessage(), e);
+ }
+ }
+
+ public AliOssFileVO get(GetObjectRequest getObjectRequest) throws Exception {
+ try (OSSObject ossObject = this.ossClient.getObject(getObjectRequest)) {
+ if (ossObject == null) {
+ throw new Exception("Failed to retrieve object from OSS.");
+ }
+
+ // 读取OSSObject的内容到ByteArrayOutputStream中
+ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+ byte[] bytes = new byte[1024]; // 根据需要调整缓冲区大小
+ int length;
+ while ((length = ossObject.getObjectContent().read(bytes)) != -1) {
+ byteArrayOutputStream.write(bytes, 0, length);
+ }
+
+ // 获取 headers
+ Map headers = new HashMap<>();
+ for (Map.Entry entry : ossObject.getObjectMetadata().getUserMetadata().entrySet()) {
+ headers.put(entry.getKey(), entry.getValue());
+ }
+ headers.put("Content-Type", ossObject.getObjectMetadata().getContentType());
+ headers.put("Content-Length", String.valueOf(ossObject.getObjectMetadata().getContentLength()));
+
+ // 设置 AliOssFileVO 对象的属性
+ AliOssFileVO fileVO = new AliOssFileVO();
+ fileVO.setFileInputSteam(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
+ fileVO.setHeaders(headers);
+ fileVO.setKey(ossObject.getKey());
+ fileVO.setBucketName(ossObject.getBucketName());
+
+ return fileVO;
+ }
+ } catch (Exception e) {
+ logger.error("Error retrieving file: {}", e.getMessage(), e);
+ throw new AliOssClientErrorException("Error retrieving file: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 文件下载
+ *
+ * @param filePath 文件路径
+ * @return 返回封装的OSS下载文件对象
+ * @throws Exception 如果下载过程中出现问题
+ */
+ public AliOssFileVO get(String filePath) throws Exception {
+ GetObjectRequest request = new GetObjectRequest(this.bucketName, filePath);
+ return get(request);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssFileVO.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssFileVO.java
new file mode 100644
index 0000000000000000000000000000000000000000..caee70b89ac14aa291b0f49d0982ab4ae2928a6d
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/domain/AliOssFileVO.java
@@ -0,0 +1,52 @@
+package com.ruoyi.alibaba.oss.domain;
+
+import java.io.InputStream;
+import java.util.Map;
+
+public class AliOssFileVO {
+ private InputStream fileInputSteam;
+ private String key;
+ private Map headers;
+ private String bucketName;
+
+ public AliOssFileVO(){}
+
+ public AliOssFileVO(InputStream fileInputSteam, String key, Map headers, String bucketName) {
+ this.fileInputSteam = fileInputSteam;
+ this.key = key;
+ this.headers = headers;
+ this.bucketName = bucketName;
+ }
+
+ public InputStream getFileInputSteam() {
+ return fileInputSteam;
+ }
+
+ public void setFileInputSteam(InputStream fileInputSteam) {
+ this.fileInputSteam = fileInputSteam;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(Map headers) {
+ this.headers = headers;
+ }
+
+ public String getBucketName() {
+ return bucketName;
+ }
+
+ public void setBucketName(String bucketName) {
+ this.bucketName = bucketName;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientErrorException.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientErrorException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9659061e3b638acb50f9692bb9ac7c4858c80d12
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientErrorException.java
@@ -0,0 +1,11 @@
+package com.ruoyi.alibaba.oss.exception;
+
+public class AliOssClientErrorException extends RuntimeException{
+ public AliOssClientErrorException(String msg){
+ super(msg);
+ }
+
+ public AliOssClientErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientNotFundException.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientNotFundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..5596ac50362a71a8998535f2a84d30bf86a6b88b
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/exception/AliOssClientNotFundException.java
@@ -0,0 +1,4 @@
+package com.ruoyi.alibaba.oss.exception;
+
+public class AliOssClientNotFundException extends RuntimeException{
+}
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/service/AliOssFileService.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/service/AliOssFileService.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e9fe73454bad006edab6af0d03122a4481299b1
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/service/AliOssFileService.java
@@ -0,0 +1,87 @@
+package com.ruoyi.alibaba.oss.service;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.ruoyi.alibaba.oss.config.AliOssConfig;
+import com.ruoyi.alibaba.oss.domain.AliOssFileVO;
+import com.ruoyi.alibaba.oss.utils.AliOssUtil;
+import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.service.file.FileService;
+import com.ruoyi.common.utils.file.FileOperateUtils;
+import com.ruoyi.common.utils.file.FileUtils;
+
+/**
+ * oss文件操作实现类
+ */
+@Component("file:strategy:oss")
+@ConditionalOnProperty(prefix = "oss", name = { "enable" }, havingValue = "true", matchIfMissing = false)
+public class AliOssFileService implements FileService {
+ @Autowired
+ private AliOssConfig aliOssConfig;
+
+ @Override
+ public String upload(String filePath, MultipartFile file) throws Exception {
+ String relativePath = null;
+ if (FileUtils.isAbsolutePath(filePath)) {
+ relativePath = FileUtils.getRelativePath(filePath);
+ } else {
+ relativePath = filePath;
+ }
+
+ String fullPath = AliOssUtil.uploadFile(aliOssConfig.getPrimary(), relativePath, file);
+ return extractFileName(fullPath);
+ }
+
+ @Override
+ public String upload(MultipartFile file, String name) throws Exception {
+ String fullPath = AliOssUtil.uploadFile(aliOssConfig.getPrimary(), name, file);
+ return extractFileName(fullPath);
+ }
+
+ @Override
+ public String upload(MultipartFile file) throws Exception {
+ String filePath = RuoYiConfig.getProfile() + File.separator + FileUtils.fastFilePath(file);
+ return upload(filePath, file);
+ }
+
+ @Override
+ public String upload(String baseDir, String fileName, MultipartFile file) throws Exception {
+ return upload(baseDir + File.pathSeparator + fileName, file);
+ }
+
+ @Override
+ public InputStream downLoad(String filepath) throws Exception {
+ AliOssFileVO file = AliOssUtil.getFile(filepath);
+ return file.getFileInputSteam();
+ }
+
+ @Override
+ public boolean deleteFile(String fileUrl) throws Exception {
+ AliOssUtil.removeFile(aliOssConfig.getPrimary(), fileUrl);
+ FileOperateUtils.deleteFileAndMd5ByFilePath(fileUrl);
+ return true;
+ }
+
+ /**
+ * 提取文件名
+ *
+ * @param fullPath 完整的文件路径
+ * @return 文件名
+ */
+ private String extractFileName(String fullPath) {
+ if (fullPath == null || !fullPath.contains("fileName=")) {
+ return fullPath;
+ }
+ int startIndex = fullPath.indexOf("fileName=") + "fileName=".length();
+ if (startIndex < 0 || startIndex >= fullPath.length()) {
+ return fullPath; // 如果没有找到 "fileName=",则返回原路径
+ }
+ return fullPath.substring(startIndex);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/utils/AliOssUtil.java b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/utils/AliOssUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..60fac1246e220a16e73c7bfbf87036eb5b621021
--- /dev/null
+++ b/ruoyi-plugins/ruoyi-alibaba-oss/src/main/java/com/ruoyi/alibaba/oss/utils/AliOssUtil.java
@@ -0,0 +1,182 @@
+package com.ruoyi.alibaba.oss.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.model.GetObjectRequest;
+import com.aliyun.oss.model.OSSObject;
+import com.ruoyi.alibaba.oss.config.AliOssConfig;
+import com.ruoyi.alibaba.oss.domain.AliOssBucket;
+import com.ruoyi.alibaba.oss.domain.AliOssFileVO;
+import com.ruoyi.alibaba.oss.exception.AliOssClientErrorException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.file.FileUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.common.utils.uuid.UUID;
+
+/**
+ * oss工具
+ */
+@Component
+public class AliOssUtil {
+
+ private static AliOssConfig aliOssConfig;
+
+ private static AliOssConfig getAliOssConfig() {
+ if (aliOssConfig == null) {
+ synchronized (AliOssUtil.class) {
+ if (aliOssConfig == null) {
+ aliOssConfig = SpringUtils.getBean(AliOssConfig.class);
+ }
+ }
+ }
+ return aliOssConfig;
+ }
+
+ /**
+ * 文件上传
+ *
+ * @param client 连接名
+ * @param file 上传的文件
+ * @return 返回上传成功的文件名
+ * @throws IOException 比如读写文件出错时
+ */
+ public static String uploadFile(String client, MultipartFile file) throws Exception {
+ String fileName = DateUtils.dateTimeNow() + UUID.fastUUID().toString().substring(0, 5) + "." + FileUtils.getExtension(file);
+ return uploadFile(client, fileName, file);
+ }
+
+ /**
+ * 文件上传
+ *
+ * @param client 连接名
+ * @param fileName 上传文件名
+ * @param file 上传的文件
+ * @return 返回上传成功的文件名
+ * @throws IOException 比如读写文件出错时
+ */
+ public static String uploadFile(String client, String fileName, MultipartFile file) throws Exception {
+ getAliOssConfig().getBucket(client).put(fileName, file);
+ return getURL(client, fileName);
+ }
+
+ /**
+ * 获取文件URL
+ *
+ * @param client 连接名
+ * @param filePath 文件路径
+ * @return 返回上传成功的文件路径
+ */
+ public static String getURL(String client, String filePath) throws Exception {
+ try {
+ StringBuilder url = new StringBuilder();
+ url.append(getAliOssConfig().getPrefix()).append("/").append(client)
+ .append("?").append("fileName=").append(filePath);
+ return url.toString();
+ } catch (Exception e) {
+ throw new AliOssClientErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * 文件删除
+ *
+ * @param filePath 上传文件路径
+ * @throws IOException 比如读写文件出错时
+ */
+ public static void removeFile(String filePath) throws Exception {
+ getAliOssConfig().getMasterBucket().remove(filePath);
+ }
+
+ /**
+ * 文件删除
+ *
+ * @param client 连接名
+ * @param filePath 上传文件路径
+ * @throws IOException 比如读写文件出错时
+ */
+ public static void removeFile(String client, String filePath) throws Exception {
+ getAliOssConfig().getBucket(client).remove(filePath);
+ }
+
+ /**
+ * 文件下载
+ *
+ * @param filePath 文件路径
+ * @return 返回封装的Oss下载文件对象
+ * @throws IOException 比如读写文件出错时
+ */
+ public static AliOssFileVO getFile(String filePath) throws Exception {
+ return getFile(null, filePath);
+ }
+
+ /**
+ * 文件下载
+ *
+ * @param client 连接名
+ * @param filePath 文件路径
+ * @return 返回封装的Oss下载文件对象
+ * @throws IOException 比如读写文件出错时
+ */
+ public static AliOssFileVO getFile(String client, String filePath) throws Exception {
+ AliOssBucket ossBucket = client == null ? getAliOssConfig().getMasterBucket() : getAliOssConfig().getBucket(client);
+ String bucketName = client == null ? getAliOssConfig().getMasterBucketName() : getAliOssConfig().getBucketName(client);
+
+ if (bucketName == null) {
+ throw new AliOssClientErrorException("参数 \"bucketName\" 为空指针");
+ }
+
+ GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, filePath);
+
+ return get(getObjectRequest, ossBucket.getOssClient());
+ }
+ /**
+ * 从OSS中获取文件并封装成AliOssFileVO对象
+ *
+ * @param getObjectRequest 获取对象请求
+ * @param ossClient OSS客户端
+ * @return 返回封装的Oss下载文件对象
+ * @throws Exception 如果获取文件失败
+ */
+ private static AliOssFileVO get(GetObjectRequest getObjectRequest, OSS ossClient) throws Exception {
+ OSSObject ossObject = ossClient.getObject(getObjectRequest);
+ if (ossObject == null) {
+ throw new Exception("Failed to retrieve object from OSS.");
+ }
+
+ // 读取OSSObject的内容到ByteArrayOutputStream中
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byte[] bytes = new byte[1024]; // 根据需要调整缓冲区大小
+ int length;
+ while ((length = ossObject.getObjectContent().read(bytes)) != -1) {
+ byteArrayOutputStream.write(bytes, 0, length);
+ }
+
+ // 获取 headers
+ Map headers = new HashMap<>();
+ for (Map.Entry entry : ossObject.getObjectMetadata().getUserMetadata().entrySet()) {
+ headers.put(entry.getKey(), entry.getValue());
+ }
+ headers.put("Content-Type", ossObject.getObjectMetadata().getContentType());
+ headers.put("Content-Length", String.valueOf(ossObject.getObjectMetadata().getContentLength()));
+
+ // 设置 AliOssFileVO 对象的属性
+ AliOssFileVO fileVO = new AliOssFileVO();
+ fileVO.setFileInputSteam(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
+ fileVO.setHeaders(headers);
+ fileVO.setKey(ossObject.getKey());
+ fileVO.setBucketName(ossObject.getBucketName());
+
+ // 关闭OSSObject
+ ossObject.close();
+
+ return fileVO;
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml
index fc7eb6d8ff58c282fa0520ec20b3c7a8741c1377..a32aacf1b35f803de0d0e2edf614e9d836281b56 100644
--- a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml
+++ b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml
@@ -55,6 +55,12 @@
com.ruoyi
ruoyi-netty
+
+
+
+ com.ruoyi
+ ruoyi-alibaba-oss
+