zhaowenyu
2025-11-26 5a66e972d39f2004f8241b6a119f246d308c70c8
2025-11-26 zwy 增加ossdock模块,机场自己单独用的minio
12 files added
3 files modified
1118 ■■■■■ changed files
ard-admin/src/main/resources/application-daqing.yml 12 ●●●●● patch | view | raw | blame | history
ard-ossdock/pom.xml 70 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/config/OssDockConfiguration.java 94 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/model/CredentialsDockDTO.java 50 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/model/MinioDockUploadResult.java 27 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/model/StsCredentialsDockDTO.java 33 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/model/enums/OssDockTypeEnum.java 25 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/IOssDockService.java 57 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/AliyunDockOssDockServiceImpl.java 115 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/AmazonS3DockDockServiceImpl.java 129 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/MinIODockDockServiceImpl.java 258 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/OssDockAspectHandler.java 31 ●●●●● patch | view | raw | blame | history
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/OssDockServiceContext.java 205 ●●●●● patch | view | raw | blame | history
ard-sample/pom.xml 4 ●●●●● patch | view | raw | blame | history
pom.xml 8 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/resources/application-daqing.yml
@@ -79,3 +79,15 @@
  expire: 3600
  region: us-east-1
  object-dir-prefix: wayline
# 机场单独用的oss配置,要http地址
ossdock:
  enable: true
  provider: minio
  endpoint: http://192.168.10.103:9011
  access-key: admin2
  secret-key: ard123456
  bucket: cloud-bucket
  expire: 3600
  region: us-east-1
  object-dir-prefix: wayline
ard-ossdock/pom.xml
New file
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ard</artifactId>
        <groupId>com.ard</groupId>
        <version>3.8.8</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ard-ossdock</artifactId>
    <description>
        机场存储模块
    </description>
    <properties>
        <aliyun-sdk-sts.version>3.1.0</aliyun-sdk-sts.version>
        <aliyun-oss.version>3.12.0</aliyun-oss.version>
    </properties>
    <dependencies>
        <!-- 通用工具-->
        <dependency>
            <groupId>com.ard</groupId>
            <artifactId>ard-common</artifactId>
        </dependency>
        <!-- minio依赖-->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
            <version>1.12.261</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-sts</artifactId>
            <version>1.12.261</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-sts</artifactId>
            <version>${aliyun-sdk-sts.version}</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>${aliyun-oss.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>
</project>
ard-ossdock/src/main/java/com/ard/ossdock/config/OssDockConfiguration.java
New file
@@ -0,0 +1,94 @@
package com.ard.ossdock.config;
import com.ard.ossdock.model.enums.OssDockTypeEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * @author sean
 * @version 0.2
 * @date 2021/12/9
 */
@Component
@ConfigurationProperties(prefix = "ossdock")
public class OssDockConfiguration {
    /**
     * @see OssDockTypeEnum
     */
    public static String provider;
    /**
     * Whether to use the object storage service.
     */
    public static boolean enable;
    /**
     * The protocol needs to be included at the beginning of the address.
     */
    public static String endpoint;
    public static String accessKey;
    public static String secretKey;
    public static String region;
    public static Long expire;
    public static String roleSessionName;
    public static String roleArn;
    public static String bucket;
    public static String objectDirPrefix;
    public void setProvider(String provider) {
        OssDockConfiguration.provider = provider;
    }
    public void setEnable(boolean enable) {
        OssDockConfiguration.enable = enable;
    }
    public void setEndpoint(String endpoint) {
        OssDockConfiguration.endpoint = endpoint;
    }
    public void setAccessKey(String accessKey) {
        OssDockConfiguration.accessKey = accessKey;
    }
    public void setSecretKey(String secretKey) {
        OssDockConfiguration.secretKey = secretKey;
    }
    public void setRegion(String region) {
        OssDockConfiguration.region = region;
    }
    public void setExpire(Long expire) {
        OssDockConfiguration.expire = expire;
    }
    public void setRoleSessionName(String roleSessionName) {
        OssDockConfiguration.roleSessionName = roleSessionName;
    }
    public void setRoleArn(String roleArn) {
        OssDockConfiguration.roleArn = roleArn;
    }
    public void setBucket(String bucket) {
        OssDockConfiguration.bucket = bucket;
    }
    public void setObjectDirPrefix(String objectDirPrefix) {
        OssDockConfiguration.objectDirPrefix = objectDirPrefix;
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/model/CredentialsDockDTO.java
New file
@@ -0,0 +1,50 @@
package com.ard.ossdock.model;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import io.minio.credentials.Credentials;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author sean
 * @version 0.2
 * @date 2021/12/7
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CredentialsDockDTO {
    private static final int DELAY = 300;
    private String accessKeyId;
    private String accessKeySecret;
    private Long expire;
    private String securityToken;
    public CredentialsDockDTO(Credentials credentials, long expire) {
        this.accessKeyId = credentials.accessKey();
        this.accessKeySecret = credentials.secretKey();
        this.securityToken = credentials.sessionToken();
        this.expire = expire - DELAY;
    }
    public CredentialsDockDTO(AssumeRoleResponse.Credentials credentials, long expire) {
        this.accessKeyId = credentials.getAccessKeyId();
        this.accessKeySecret = credentials.getAccessKeySecret();
        this.securityToken = credentials.getSecurityToken();
        this.expire = expire - DELAY;
    }
    public CredentialsDockDTO(com.amazonaws.services.securitytoken.model.Credentials credentials) {
        this.accessKeyId = credentials.getAccessKeyId();
        this.accessKeySecret = credentials.getSecretAccessKey();
        this.securityToken = credentials.getSessionToken();
        this.expire = (credentials.getExpiration().getTime() - System.currentTimeMillis()) / 1000 - DELAY;
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/model/MinioDockUploadResult.java
New file
@@ -0,0 +1,27 @@
package com.ard.ossdock.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * Description: Minio上传结果
 * Author: lsy
 * Date: 2025年10月13日10:42
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MinioDockUploadResult {
    /** 新文件名(上传后保存名) */
    private String newFileName;
    /** 文件完整访问 URL */
    private String url;
    /** 上传前原始文件名 */
    private String originalFilename;
}
ard-ossdock/src/main/java/com/ard/ossdock/model/StsCredentialsDockDTO.java
New file
@@ -0,0 +1,33 @@
package com.ard.ossdock.model;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author sean
 * @version 0.2
 * @date 2021/12/7
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class StsCredentialsDockDTO {
    private String bucket;
    private CredentialsDockDTO credentials;
    private String endpoint;
    private String objectKeyPrefix;
    private String provider;
    private String region;
}
ard-ossdock/src/main/java/com/ard/ossdock/model/enums/OssDockTypeEnum.java
New file
@@ -0,0 +1,25 @@
package com.ard.ossdock.model.enums;
/**
 * @author sean
 * @version 1.0
 * @date 2022/5/30
 */
public enum OssDockTypeEnum {
    ALIYUN("ali"),
    AWS("aws"),
    MINIO("minio");
    private String type;
    OssDockTypeEnum(String type) {
        this.type = type;
    }
    public String getType() {
        return type;
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/service/IOssDockService.java
New file
@@ -0,0 +1,57 @@
package com.ard.ossdock.service;
import com.ard.ossdock.model.CredentialsDockDTO;
import java.io.InputStream;
import java.net.URL;
/**
 * @author sean
 * @version 0.3
 * @date 2021/12/23
 */
public interface IOssDockService {
    public String getOssType();
    /**
     * Get temporary credentials.
     *
     * @return
     */
    public CredentialsDockDTO getCredentials();
    /**
     * Get the address of the object based on the bucket name and the object name.
     *
     * @param bucket    bucket name
     * @param objectKey object name
     * @return download link
     */
    public URL getObjectUrl(String bucket, String objectKey);
    /**
     * Deletes the object in the storage bucket.
     *
     * @param bucket
     * @param objectKey
     * @return
     */
    public Boolean deleteObject(String bucket, String objectKey);
    /**
     * Get the contents of an object.
     *
     * @param bucket
     * @param objectKey
     * @return
     */
    public InputStream getObject(String bucket, String objectKey);
    public void putObject(String bucket, String objectKey, InputStream input);
    public void putObject(String bucket, String objectKey, InputStream input, String contentType);
    public void createClient();
}
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/AliyunDockOssDockServiceImpl.java
New file
@@ -0,0 +1,115 @@
package com.ard.ossdock.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.ard.ossdock.config.OssDockConfiguration;
import com.ard.ossdock.model.CredentialsDockDTO;
import com.ard.ossdock.model.enums.OssDockTypeEnum;
import com.ard.ossdock.service.IOssDockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Objects;
/**
 * @author sean
 * @version 0.3
 * @date 2021/12/23
 */
@Service
@Slf4j
public class AliyunDockOssDockServiceImpl implements IOssDockService {
    private OSS ossClient;
    @Override
    public String getOssType() {
        return OssDockTypeEnum.ALIYUN.getType();
    }
    @Override
    public CredentialsDockDTO getCredentials() {
        try {
            DefaultProfile profile = DefaultProfile.getProfile(
                    OssDockConfiguration.region, OssDockConfiguration.accessKey, OssDockConfiguration.secretKey);
            IAcsClient client = new DefaultAcsClient(profile);
            AssumeRoleRequest request = new AssumeRoleRequest();
            request.setDurationSeconds(OssDockConfiguration.expire);
            request.setRoleArn(OssDockConfiguration.roleArn);
            request.setRoleSessionName(OssDockConfiguration.roleSessionName);
            AssumeRoleResponse response = client.getAcsResponse(request);
            return new CredentialsDockDTO(response.getCredentials(), OssDockConfiguration.expire);
        } catch (ClientException e) {
            log.debug("Failed to obtain sts.");
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public URL getObjectUrl(String bucket, String objectKey) {
        // First check if the object can be fetched.
        boolean isExist = ossClient.doesObjectExist(bucket, objectKey);
        if (!isExist) {
            throw new OSSException("The object does not exist.");
        }
        return ossClient.generatePresignedUrl(bucket, objectKey,
                new Date(System.currentTimeMillis() + OssDockConfiguration.expire * 1000));
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        if (!ossClient.doesObjectExist(bucket, objectKey)) {
            return true;
        }
        ossClient.deleteObject(bucket, objectKey);
        return true;
    }
    @Override
    public InputStream getObject(String bucket, String objectKey) {
        return ossClient.getObject(bucket, objectKey).getObjectContent();
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        if (ossClient.doesObjectExist(bucket, objectKey)) {
            throw new RuntimeException("The filename already exists.");
        }
        PutObjectResult objectResult = ossClient.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
        log.info("Upload File: {}", objectResult.getETag());
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input, String contentType) {
    }
    @Override
    public void createClient() {
        if (Objects.nonNull(this.ossClient)) {
            return;
        }
        this.ossClient = new OSSClientBuilder()
                .build(OssDockConfiguration.endpoint, OssDockConfiguration.accessKey, OssDockConfiguration.secretKey);
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/AmazonS3DockDockServiceImpl.java
New file
@@ -0,0 +1,129 @@
package com.ard.ossdock.service.impl;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.amazonaws.services.securitytoken.model.Credentials;
import com.ard.ossdock.config.OssDockConfiguration;
import com.ard.ossdock.model.CredentialsDockDTO;
import com.ard.ossdock.model.enums.OssDockTypeEnum;
import com.ard.ossdock.service.IOssDockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.Objects;
/**
 * @author sean
 * @version 1.0
 * @date 2022/4/27
 */
@Slf4j
@Service
public class AmazonS3DockDockServiceImpl implements IOssDockService {
    private AmazonS3 client;
    @Override
    public String getOssType() {
        return OssDockTypeEnum.AWS.getType();
    }
    @Override
    public CredentialsDockDTO getCredentials() {
        AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(
                        new BasicAWSCredentials(OssDockConfiguration.accessKey, OssDockConfiguration.secretKey)))
                .withRegion(OssDockConfiguration.region).build();
        AssumeRoleRequest request = new AssumeRoleRequest()
                .withRoleArn(OssDockConfiguration.roleArn)
                .withRoleSessionName(OssDockConfiguration.roleSessionName)
                .withDurationSeconds(Math.toIntExact(OssDockConfiguration.expire));
        AssumeRoleResult result = stsClient.assumeRole(request);
        Credentials credentials = result.getCredentials();
        return new CredentialsDockDTO(credentials);
    }
    @Override
    public URL getObjectUrl(String bucket, String objectKey) {
        return client.generatePresignedUrl(bucket, objectKey,
                new Date(System.currentTimeMillis() + OssDockConfiguration.expire * 1000), HttpMethod.GET);
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        if (!client.doesObjectExist(bucket, objectKey)) {
            return true;
        }
        client.deleteObject(bucket, objectKey);
        return true;
    }
    @Override
    public InputStream getObject(String bucket, String objectKey) {
        return client.getObject(bucket, objectKey).getObjectContent().getDelegateStream();
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        if (client.doesObjectExist(bucket, objectKey)) {
            throw new RuntimeException("The filename already exists.");
        }
        PutObjectResult objectResult = client.putObject(new PutObjectRequest(bucket, objectKey, input, new ObjectMetadata()));
        log.info("Upload File: {}", objectResult.toString());
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input, String contentType) {
    }
    @Override
    public void createClient() {
        if (Objects.nonNull(this.client)) {
            return;
        }
        this.client = AmazonS3ClientBuilder.standard()
                .withCredentials(
                        new AWSStaticCredentialsProvider(
                                new BasicAWSCredentials(OssDockConfiguration.accessKey, OssDockConfiguration.secretKey)))
                .withRegion(OssDockConfiguration.region)
                .build();
    }
//    /**
//     * Configuring cross-origin resource sharing
//     */
//    @PostConstruct
//    private void configCORS() {
//        if (!OssConfiguration.enable || !OssTypeEnum.AWS.getType().equals(OssConfiguration.provider)) {
//            return;
//        }
//        List<CORSRule.AllowedMethods> allowedMethods = new ArrayList<>();
//        allowedMethods.add(CORSRule.AllowedMethods.GET);
//        allowedMethods.add(CORSRule.AllowedMethods.POST);
//        allowedMethods.add(CORSRule.AllowedMethods.DELETE);
//
//        CORSRule rule = new CORSRule()
//                .withId("CORSAccessRule")
//                .withAllowedOrigins(List.of("*"))
//                .withAllowedHeaders(List.of(AuthInterceptor.PARAM_TOKEN))
//                .withAllowedMethods(allowedMethods);
//
//        client.setBucketCrossOriginConfiguration(OssConfiguration.bucket,
//                new BucketCrossOriginConfiguration().withRules(rule));
//
//    }
}
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/MinIODockDockServiceImpl.java
New file
@@ -0,0 +1,258 @@
package com.ard.ossdock.service.impl;
import com.ard.ossdock.config.OssDockConfiguration;
import com.ard.ossdock.model.CredentialsDockDTO;
import com.ard.ossdock.model.enums.OssDockTypeEnum;
import com.ard.ossdock.service.IOssDockService;
import io.minio.*;
import io.minio.credentials.AssumeRoleProvider;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.springframework.stereotype.Service;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Objects;
import static com.ard.ossdock.config.OssDockConfiguration.*;
/**
 * @author sean
 * @version 0.3
 * @date 2021/12/23
 */
@Service
@Slf4j
public class MinIODockDockServiceImpl implements IOssDockService {
    private MinioClient client;
    @Override
    public String getOssType() {
        return OssDockTypeEnum.MINIO.getType();
    }
    public CredentialsDockDTO getCredentials111() {
        try {
            AssumeRoleProvider provider = new AssumeRoleProvider(endpoint, accessKey,
                    secretKey, Math.toIntExact(OssDockConfiguration.expire),
                    null, OssDockConfiguration.region, null, null, null, null);
            return new CredentialsDockDTO(provider.fetch(), OssDockConfiguration.expire);
        } catch (NoSuchAlgorithmException e) {
            log.debug("Failed to obtain sts.");
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public CredentialsDockDTO getCredentials() {
        try {
            // 创建自签证书的 OkHttpClient
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
                        @Override
                        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
                        @Override
                        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; }
                    }
            };
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, trustAllCerts, new SecureRandom());
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .build();
            // 构建 AssumeRoleProvider,使用自定义 httpClient
            AssumeRoleProvider provider = new AssumeRoleProvider(
                    endpoint,
                    accessKey,
                    secretKey,
                    Math.toIntExact(OssDockConfiguration.expire),
                    null,
                    OssDockConfiguration.region,
                    null,
                    null,
                    null,
                    httpClient
            );
            // 获取 STS 凭证
            var stsCredentials = provider.fetch();
            return new CredentialsDockDTO(stsCredentials, OssDockConfiguration.expire);
        } catch (ProviderException e) {
            log.error("Failed to parse STS response", e);
            throw new RuntimeException("STS response parse failed", e);
        } catch (NoSuchAlgorithmException | java.security.KeyManagementException e) {
            log.error("Failed to create custom SSL context", e);
            throw new RuntimeException("SSL setup failed", e);
        }
    }
    @Override
    public URL getObjectUrl(String bucket, String objectKey) {
        try {
            return new URL(
                    client.getPresignedObjectUrl(
                            GetPresignedObjectUrlArgs.builder()
                                    .method(Method.GET)
                                    .bucket(bucket)
                                    .object(objectKey)
                                    .expiry(Math.toIntExact(OssDockConfiguration.expire))
                                    .build()));
        } catch (ErrorResponseException | InsufficientDataException | InternalException |
                 InvalidKeyException | InvalidResponseException | IOException |
                 NoSuchAlgorithmException | XmlParserException | ServerException e) {
            throw new RuntimeException("The file does not exist on the OssConfiguration.");
        }
    }
    @Override
    public Boolean deleteObject(String bucket, String objectKey) {
        try {
            client.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectKey)
                    .build());
        } catch (MinioException | NoSuchAlgorithmException | IOException | InvalidKeyException e) {
            log.error("Failed to delete file.");
            e.printStackTrace();
            return false;
        }
        return true;
    }
    @Override
    public InputStream getObject(String bucket, String objectKey) {
        try {
            GetObjectResponse object = client.getObject(GetObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectKey)
                    .build());
            return new ByteArrayInputStream(object.readAllBytes());
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
            e.printStackTrace();
        }
        return InputStream.nullInputStream();
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input) {
        try {
            client.statObject(StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectKey)
                    .build());
            throw new RuntimeException("The filename already exists.");
        } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
            log.info("The file does not exist, start uploading.");
            try {
                // 设置 partSize 为 10MB
                long partSize = 10 * 1024 * 1024;
                ObjectWriteResponse response = client.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucket)
                                .object(objectKey)
                                .stream(input, -1, partSize)
                                .build());
                log.info("Upload File: {}", response.etag());
            } catch (MinioException | IOException | InvalidKeyException | NoSuchAlgorithmException ex) {
                log.error("Failed to upload File {}.", objectKey);
                ex.printStackTrace();
            }
        }
    }
    @Override
    public void putObject(String bucket, String objectKey, InputStream input, String contentType) {
        try {
            // 检查文件是否已经存在
            client.statObject(StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectKey)
                    .build());
            throw new RuntimeException("The file already exists: " + objectKey);
        } catch (ErrorResponseException e) {
            // 如果文件不存在,则继续上传
            log.info("The file does not exist, start uploading.");
            try {
                // 设置 partSize 为 10MB
                long partSize = 10 * 1024 * 1024; // 每块 10MB
                PutObjectArgs.Builder putArgsBuilder = PutObjectArgs.builder()
                        .bucket(bucket)
                        .object(objectKey)
                        .stream(input, -1, partSize);
                // 如果提供了 Content-Type,则设置该类型
                if (contentType != null && !contentType.isEmpty()) {
                    putArgsBuilder.contentType(contentType);
                }
                // 上传文件
                ObjectWriteResponse response = client.putObject(putArgsBuilder.build());
                log.info("Uploaded file successfully: {}, ETag: {}", objectKey, response.etag());
            } catch (MinioException | IOException | InvalidKeyException | NoSuchAlgorithmException ex) {
                log.error("Failed to upload file: {}", objectKey, ex);
                throw new RuntimeException("File upload failed: " + objectKey, ex);
            }
        } catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
            // 如果 statObject 发生错误,说明服务端错误或其他问题
            log.error("Failed to check if file exists: {}", objectKey, e);
            throw new RuntimeException("Failed to check if file exists: " + objectKey, e);
        }
    }
    @Override
    public void createClient() {
        if (Objects.nonNull(this.client)) {
            return;
        }
        try {
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
                        @Override
                        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
                        @Override
                        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; }
                    }
            };
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new SecureRandom());
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .build();
            this.client = MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .httpClient(httpClient)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException("Failed to create unsafe MinioClient", e);
        }
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/OssDockAspectHandler.java
New file
@@ -0,0 +1,31 @@
package com.ard.ossdock.service.impl;
import com.ard.ossdock.config.OssDockConfiguration;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author sean
 * @version 1.1
 * @date 2022/6/20
 */
@Component
@Aspect
public class OssDockAspectHandler {
    @Autowired
    private OssDockServiceContext ossDockServiceContext;
    @Before("execution(public * com.ard.ossdock.service.impl.OssDockServiceContext.*(..))")
    public void before() {
        if (!OssDockConfiguration.enable) {
            throw new IllegalArgumentException("Please enable OssConfiguration.");
        }
        if (this.ossDockServiceContext.getOssService() == null) {
            throw new IllegalArgumentException("Please check the OssConfiguration configuration.");
        }
        this.ossDockServiceContext.createClient();
    }
}
ard-ossdock/src/main/java/com/ard/ossdock/service/impl/OssDockServiceContext.java
New file
@@ -0,0 +1,205 @@
package com.ard.ossdock.service.impl;
import com.ard.ossdock.config.OssDockConfiguration;
import com.ard.common.exception.file.FileNameLengthLimitExceededException;
import com.ard.common.utils.file.FileUploadUtils;
import com.ard.common.utils.file.MimeTypeUtils;
import com.ard.ossdock.model.CredentialsDockDTO;
import com.ard.ossdock.model.enums.OssDockTypeEnum;
import com.ard.ossdock.service.IOssDockService;
import lombok.Cleanup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import static com.ard.common.utils.file.FileUploadUtils.assertAllowed;
import static com.ard.common.utils.file.FileUploadUtils.extractFilename;
/**
 * @author sean
 * @version 1.0
 * @date 2022/5/30
 */
@Service
public class OssDockServiceContext {
    private IOssDockService ossService;
    @Autowired
    public OssDockServiceContext(List<IOssDockService> ossServices, OssDockConfiguration configuration) {
        if (!OssDockConfiguration.enable) {
            return;
        }
        this.ossService = ossServices.stream()
                .filter(ossService -> ossService.getOssType().equals(OssDockConfiguration.provider))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Oss provider is illegal. Optional: " +
                        Arrays.toString(Arrays.stream(OssDockTypeEnum.values()).map(OssDockTypeEnum::getType).toArray())));
    }
    IOssDockService getOssService() {
        return this.ossService;
    }
    public CredentialsDockDTO getCredentials() {
        return this.ossService.getCredentials();
    }
    public URL getObjectUrl(String bucket, String objectKey) {
        if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) {
            throw new IllegalArgumentException();
        }
        return this.ossService.getObjectUrl(bucket, objectKey);
    }
    public Boolean deleteObject(String bucket, String objectKey) {
        return this.ossService.deleteObject(bucket, objectKey);
    }
    public InputStream getObject(String bucket, String objectKey) {
        return this.ossService.getObject(bucket, objectKey);
    }
    public void putObject(String bucket, String objectKey, InputStream stream) {
        this.ossService.putObject(bucket, objectKey, stream);
    }
    void createClient() {
        this.ossService.createClient();
    }
    /**
     * 以默认BucketName配置上传到Minio服务器
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public String upload(MultipartFile file) throws IOException {
        try {
            return upload(OssDockConfiguration.bucket, file);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }
    /**
     * 自定义bucketName配置上传到Minio服务器
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public String upload(String bucket, MultipartFile file) throws IOException {
        try {
            return upload(bucket, null, file);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }
    /**
     * ✅ 通用上传方法:
     * - bucket 必传
     * - object 可选(为空则自动生成)
     * - 返回永久访问 URL
     */
    public String upload(String bucket, String object, MultipartFile file) throws IOException {
        try {
            // 校验文件名长度
            int fileNameLength = file.getOriginalFilename().length();
            if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
                throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
            }
            // 检查文件扩展名是否合法
            assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
            // ✅ 如果 object 为空,则自动生成文件名
            String objectName = (object == null || object.isEmpty())
                    ? extractFilename(file)
                    : object;
            // 通过文件扩展名获取 MIME 类型
            String contentType = getMimeTypeByExtension(file.getOriginalFilename());
            @Cleanup
            InputStream inputStream = file.getInputStream();
            // 上传文件到 Minio
            this.ossService.putObject(bucket, objectName, inputStream, contentType);
            return buildUrl(bucket, objectName);
        } catch (Exception e) {
            throw new IOException("Upload failed: " + e.getMessage(), e);
        }
    }
    public String upload(String bucket, String objectKey, InputStream input) {
        try {
            // 上传文件
            this.ossService.putObject(bucket, objectKey, input);
            return buildUrl(bucket, objectKey);
        } catch (Exception e) {
            throw new RuntimeException("Upload failed: " + e.getMessage(), e);
        }
    }
    public String upload(String bucket, String objectKey, InputStream input, String contentType) {
        try {
            // 上传文件时指定 Content-Type
            this.ossService.putObject(bucket, objectKey, input, contentType);
            return buildUrl(bucket, objectKey);
        } catch (Exception e) {
            throw new RuntimeException("Upload failed: " + e.getMessage(), e);
        }
    }
    // 获取文件 MIME 类型
    public String getMimeTypeByExtension(String fileName) {
        // 获取文件扩展名
        String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
        // 根据文件扩展名返回 MIME 类型
        switch (extension) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "pdf":
                return "application/pdf";
            case "txt":
                return "text/plain";
            case "html":
                return "text/html";
            case "json":
                return "application/json";
            case "csv":
                return "text/csv";
            case "zip":
                return "application/zip";
            // 添加更多的扩展名映射
            default:
                return "application/octet-stream";  // 默认 MIME 类型
        }
    }
    // 构建url
    private String buildUrl(String bucket, String key) {
        String base = OssDockConfiguration.endpoint;
        if (!base.endsWith("/")) {
            base += "/";
        }
        return base + bucket + "/" + key;
    }
}
ard-sample/pom.xml
@@ -118,5 +118,9 @@
            <groupId>com.ard</groupId>
            <artifactId>ard-route</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ard</groupId>
            <artifactId>ard-ossdock</artifactId>
        </dependency>
    </dependencies>
</project>
pom.xml
@@ -218,7 +218,12 @@
                <artifactId>ard-oss</artifactId>
                <version>${ard.version}</version>
            </dependency>
            <!-- 机场存储模块-->
            <dependency>
                <groupId>com.ard</groupId>
                <artifactId>ard-ossdock</artifactId>
                <version>${ard.version}</version>
            </dependency>
            <!-- okhttp3-->
            <dependency>
@@ -270,6 +275,7 @@
        <module>ard-route</module>
        <module>ard-device</module>
        <module>ard-oss</module>
        <module>ard-ossdock</module>
    </modules>
    <build>