diff --git a/pom.xml b/pom.xml
index 573c936a8..3616f1cfd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -422,6 +422,13 @@
spring-boot-starter-test
test
+
+
+
+ com.qcloud
+ cos_api
+ 5.6.227
+
diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/util/CosUtil.java b/src/main/java/com/genersoft/iot/vmp/aiot/util/CosUtil.java
new file mode 100644
index 000000000..0b0d1aaac
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/aiot/util/CosUtil.java
@@ -0,0 +1,96 @@
+package com.genersoft.iot.vmp.aiot.util;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.http.HttpProtocol;
+import com.qcloud.cos.region.Region;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import java.net.URL;
+import java.util.Date;
+
+@Slf4j
+@Component
+public class CosUtil {
+
+ @Value("${ai.cos.secret-id:${COS_SECRET_ID:}}")
+ private String secretId;
+
+ @Value("${ai.cos.secret-key:${COS_SECRET_KEY:}}")
+ private String secretKey;
+
+ @Value("${ai.cos.region:${COS_REGION:ap-beijing}}")
+ private String region;
+
+ @Value("${ai.cos.bucket:${COS_BUCKET:}}")
+ private String bucket;
+
+ private COSClient cosClient;
+
+ @PostConstruct
+ public void init() {
+ if (secretId == null || secretId.isEmpty() || secretKey == null || secretKey.isEmpty()) {
+ log.warn("[COS] 未配置 COS 凭证(COS_SECRET_ID/COS_SECRET_KEY),图片代理功能不可用");
+ return;
+ }
+ if (bucket == null || bucket.isEmpty()) {
+ log.warn("[COS] 未配置 COS_BUCKET,图片代理功能不可用");
+ return;
+ }
+ try {
+ COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
+ ClientConfig config = new ClientConfig(new Region(region));
+ config.setHttpProtocol(HttpProtocol.https);
+ cosClient = new COSClient(cred, config);
+ log.info("[COS] 客户端初始化成功: region={}, bucket={}", region, bucket);
+ } catch (Exception e) {
+ log.error("[COS] 客户端初始化失败: {}", e.getMessage());
+ }
+ }
+
+ @PreDestroy
+ public void destroy() {
+ if (cosClient != null) {
+ cosClient.shutdown();
+ }
+ }
+
+ /**
+ * 生成预签名下载 URL
+ *
+ * @param objectKey COS 对象路径
+ * @param expireSeconds 有效期(秒)
+ * @return presigned URL,失败返回 null
+ */
+ public String generatePresignedUrl(String objectKey, int expireSeconds) {
+ if (cosClient == null) {
+ log.warn("[COS] 客户端未初始化,无法生成 presigned URL");
+ return null;
+ }
+ try {
+ Date expiration = new Date(System.currentTimeMillis() + expireSeconds * 1000L);
+ URL url = cosClient.generatePresignedUrl(bucket, objectKey, expiration);
+ return url.toString();
+ } catch (Exception e) {
+ log.error("[COS] 生成 presigned URL 失败: key={}, error={}", objectKey, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 生成预签名下载 URL(默认 1 小时有效)
+ */
+ public String generatePresignedUrl(String objectKey) {
+ return generatePresignedUrl(objectKey, 3600);
+ }
+
+ public boolean isAvailable() {
+ return cosClient != null;
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 59d170601..671512501 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -124,6 +124,11 @@ ai:
screenshot:
# Edge截图回调地址(WVP外部可访问地址,Edge通过此地址回调截图结果)
callback-url: http://124.221.55.225:18080
+ cos:
+ secret-id:
+ secret-key:
+ region: ap-beijing
+ bucket:
mqtt:
# MQTT推送开关
enabled: false
diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml
index 4a24fa674..fb16a68b0 100644
--- a/src/main/resources/application-docker.yml
+++ b/src/main/resources/application-docker.yml
@@ -86,6 +86,11 @@ ai:
enabled: ${AI_SERVICE_ENABLED:false}
screenshot:
callback-url: ${AI_SCREENSHOT_CALLBACK_URL:}
+ cos:
+ secret-id: ${COS_SECRET_ID:}
+ secret-key: ${COS_SECRET_KEY:}
+ region: ${COS_REGION:ap-beijing}
+ bucket: ${COS_BUCKET:}
mqtt:
enabled: ${AI_MQTT_ENABLED:false}
broker: ${AI_MQTT_BROKER:tcp://127.0.0.1:1883}