feat: cos upload support

This commit is contained in:
2025-12-19 11:43:05 +08:00
parent f9eaa441a3
commit 9a12ef5e2e
11 changed files with 571 additions and 8 deletions

View File

@@ -78,6 +78,13 @@
<artifactId>ruoyi-common-oss</artifactId>
</dependency>
<!-- 腾讯云COS临时密钥生成 -->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos-sts_api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,46 @@
package org.dromara.inspection.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 腾讯云COS配置属性
*
* @author LionLi
*/
@Data
@Component
@ConfigurationProperties(prefix = "tencent.cos")
public class TencentCosProperties {
/**
* 腾讯云SecretId
*/
private String secretId;
/**
* 腾讯云SecretKey
*/
private String secretKey;
/**
* 临时密钥有效期(秒),默认1800秒=30分钟
*/
private Integer durationSeconds = 1800;
/**
* 存储桶名称
*/
private String bucket;
/**
* 存储桶所在地域
*/
private String region;
/**
* 腾讯云AppId
*/
private String appId;
}

View File

@@ -0,0 +1,125 @@
package org.dromara.inspection.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Policy;
import com.tencent.cloud.Response;
import com.tencent.cloud.Statement;
import com.tencent.cloud.cos.util.Jackson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.inspection.config.TencentCosProperties;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* 腾讯云COS临时凭证Controller
*
* @author LionLi
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/inspection/cos")
public class TencentCosController {
private final TencentCosProperties cosProperties;
private static final String CACHE_KEY = "inspection:cos:credential";
private static final int CACHE_EXPIRE_SECONDS = 1500; // 缓存25分钟,临时密钥30分钟过期
/**
* 获取腾讯云COS临时上传凭证
*/
@SaCheckPermission("inspection:step:edit")
@Log(title = "获取COS临时凭证", businessType = BusinessType.OTHER)
@GetMapping("/credential")
public R<Response> getCredential() {
// 先从缓存获取
Response cachedCredential = RedisUtils.getCacheObject(CACHE_KEY);
if (cachedCredential != null) {
return R.ok(cachedCredential);
}
try {
// 构建配置参数
TreeMap<String, Object> config = new TreeMap<>();
config.put("secretId", cosProperties.getSecretId());
config.put("secretKey", cosProperties.getSecretKey());
config.put("durationSeconds", cosProperties.getDurationSeconds());
config.put("bucket", cosProperties.getBucket());
config.put("region", cosProperties.getRegion());
// 初始化 policy
Policy policy = new Policy();
// 开始构建一条 statement
Statement statement = new Statement();
// 声明设置的结果是允许操作
statement.setEffect("allow");
// 添加操作权限
statement.addActions(new String[]{
// 简单上传
"cos:PutObject",
"cos:PostObject",
// 分块上传
"cos:InitiateMultipartUpload",
"cos:ListMultipartUploads",
"cos:ListParts",
"cos:UploadPart",
"cos:CompleteMultipartUpload"
});
// 设置允许操作的资源路径(限定只能上传到audio目录)
// 格式: qcs::cos:{region}:uid/{appid}:{bucket}/{path}
statement.addResources(new String[]{
"qcs::cos:" + cosProperties.getRegion() +
":uid/" + cosProperties.getAppId() +
":" + cosProperties.getBucket() +
"/audio/*"
});
// 把一条 statement 添加到 policy
policy.addStatement(statement);
// 将 Policy 实例转化成 String
config.put("policy", Jackson.toJsonPrettyString(policy));
// 获取临时密钥
Response response = CosStsClient.getCredential(config);
// 缓存凭证(25分钟,临时密钥30分钟过期)
RedisUtils.setCacheObject(CACHE_KEY, response, Duration.ofSeconds(CACHE_EXPIRE_SECONDS));
return R.ok(response);
} catch (Exception e) {
log.error("获取临时COS凭证失败", e);
return R.fail("获取临时凭证失败:" + e.getMessage());
}
}
/**
* 获取COS配置信息(供前端使用)
*/
@SaCheckPermission("inspection:step:edit")
@GetMapping("/config")
public R<Map<String, String>> getConfig() {
Map<String, String> config = new HashMap<>();
config.put("bucket", cosProperties.getBucket());
config.put("region", cosProperties.getRegion());
return R.ok(config);
}
}