init
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package com.cadlabel.service.client;
|
||||
|
||||
import com.cadlabel.common.exception.BizException;
|
||||
import com.cadlabel.config.CosProperties;
|
||||
import com.qcloud.cos.COSClient;
|
||||
import com.qcloud.cos.ClientConfig;
|
||||
import com.qcloud.cos.auth.BasicCOSCredentials;
|
||||
import com.qcloud.cos.exception.CosClientException;
|
||||
import com.qcloud.cos.model.ObjectMetadata;
|
||||
import com.qcloud.cos.model.PutObjectRequest;
|
||||
import com.qcloud.cos.region.Region;
|
||||
import com.tencent.cloud.CosStsClient;
|
||||
import com.tencent.cloud.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.util.TreeMap;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TencentCosObjectStorageClient implements CosObjectStorageClient, DisposableBean {
|
||||
|
||||
private static final String[] ALLOWED_ACTIONS = new String[]{
|
||||
"name/cos:PutObject",
|
||||
"name/cos:InitiateMultipartUpload",
|
||||
"name/cos:ListMultipartUploads",
|
||||
"name/cos:ListParts",
|
||||
"name/cos:UploadPart",
|
||||
"name/cos:CompleteMultipartUpload",
|
||||
"name/cos:AbortMultipartUpload"
|
||||
};
|
||||
|
||||
private final CosProperties cosProperties;
|
||||
private final COSClient cosClient;
|
||||
|
||||
public TencentCosObjectStorageClient(CosProperties cosProperties) {
|
||||
this.cosProperties = cosProperties;
|
||||
this.cosClient = createCosClient(cosProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemporaryCredentials issueTemporaryCredentials(String keyPrefix, Duration duration) {
|
||||
validateConfiguration();
|
||||
int durationSeconds = validateDuration(duration);
|
||||
TreeMap<String, Object> config = new TreeMap<>();
|
||||
config.put("secretId", cosProperties.secretId());
|
||||
config.put("secretKey", cosProperties.secretKey());
|
||||
config.put("bucket", cosProperties.bucket());
|
||||
config.put("region", cosProperties.region());
|
||||
config.put("durationSeconds", durationSeconds);
|
||||
config.put("allowPrefixes", new String[]{normalizeKeyPrefix(keyPrefix) + "*"});
|
||||
config.put("allowActions", ALLOWED_ACTIONS);
|
||||
try {
|
||||
Response response = CosStsClient.getCredential(config);
|
||||
if (response == null || response.credentials == null
|
||||
|| !StringUtils.hasText(response.credentials.tmpSecretId)
|
||||
|| !StringUtils.hasText(response.credentials.tmpSecretKey)
|
||||
|| !StringUtils.hasText(response.credentials.sessionToken)) {
|
||||
throw new BizException(HttpStatus.BAD_GATEWAY.value(), "COS_UPLOAD_CREDENTIALS_FAILED", "COS 未返回完整的临时上传凭证");
|
||||
}
|
||||
return new TemporaryCredentials(
|
||||
response.credentials.tmpSecretId,
|
||||
response.credentials.tmpSecretKey,
|
||||
response.credentials.sessionToken,
|
||||
response.startTime,
|
||||
response.expiredTime
|
||||
);
|
||||
} catch (IOException exception) {
|
||||
log.error("Failed to issue COS upload credentials. keyPrefix={}", keyPrefix, exception);
|
||||
throw new BizException(HttpStatus.BAD_GATEWAY.value(), "COS_UPLOAD_CREDENTIALS_FAILED",
|
||||
"申请 COS 临时上传凭证失败: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putObject(String objectKey, InputStream inputStream, long contentLength, String contentType) {
|
||||
validateConfiguration();
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentLength(contentLength);
|
||||
if (StringUtils.hasText(contentType)) {
|
||||
metadata.setContentType(contentType);
|
||||
}
|
||||
try {
|
||||
cosClient.putObject(new PutObjectRequest(cosProperties.bucket(), normalizeKeyPrefix(objectKey), inputStream, metadata));
|
||||
} catch (CosClientException exception) {
|
||||
log.error("Failed to upload COS object. objectKey={}", objectKey, exception);
|
||||
throw new BizException(HttpStatus.BAD_GATEWAY.value(), "COS_OBJECT_UPLOAD_FAILED",
|
||||
"上传 COS 对象失败: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
cosClient.shutdown();
|
||||
}
|
||||
|
||||
private static COSClient createCosClient(CosProperties cosProperties) {
|
||||
String region = StringUtils.hasText(cosProperties.region()) ? cosProperties.region().trim() : "ap-beijing";
|
||||
return new COSClient(
|
||||
new BasicCOSCredentials(defaultText(cosProperties.secretId()), defaultText(cosProperties.secretKey())),
|
||||
new ClientConfig(new Region(region))
|
||||
);
|
||||
}
|
||||
|
||||
private void validateConfiguration() {
|
||||
requireText(cosProperties.secretId(), "APP_COS_SECRET_ID");
|
||||
requireText(cosProperties.secretKey(), "APP_COS_SECRET_KEY");
|
||||
requireText(cosProperties.bucket(), "APP_COS_BUCKET");
|
||||
requireText(cosProperties.region(), "APP_COS_REGION");
|
||||
}
|
||||
|
||||
private int validateDuration(Duration duration) {
|
||||
if (duration == null || duration.isZero() || duration.isNegative()) {
|
||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "COS_CONFIG_ERROR", "COS 上传会话有效期配置非法");
|
||||
}
|
||||
long seconds = duration.toSeconds();
|
||||
if (seconds > Integer.MAX_VALUE) {
|
||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "COS_CONFIG_ERROR", "COS 上传会话有效期配置过大");
|
||||
}
|
||||
return (int) seconds;
|
||||
}
|
||||
|
||||
private String normalizeKeyPrefix(String keyPrefix) {
|
||||
String normalized = defaultText(keyPrefix).trim().replace("\\", "/");
|
||||
while (normalized.startsWith("/")) {
|
||||
normalized = normalized.substring(1);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void requireText(String value, String envName) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "COS_CONFIG_ERROR", "COS 配置缺失: " + envName);
|
||||
}
|
||||
}
|
||||
|
||||
private static String defaultText(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user