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>