From 18db20099a342a0c16d42f087cac01dae8f5c03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Wed, 26 Aug 2026 13:37:11 +0800 Subject: [PATCH] =?UTF-8?q?OSS=20=E7=BB=93=E6=9E=9C=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=88=86=E6=B5=81=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20aiimage.oss.download-endpoint=20=E9=85=8D=E7=BD=AE=EF=BC=8Cr?= =?UTF-8?q?esult/=20=E5=89=8D=E7=BC=80=E5=AF=B9=E8=B1=A1=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E4=B8=8B=E8=BD=BD=E5=9F=9F=E5=90=8D=E7=9B=B4?= =?UTF-8?q?=E9=93=BE=EF=BC=88=E9=BB=98=E8=AE=A4=E6=9C=AA=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=97=B6=E5=9B=9E=E9=80=80=E5=8E=9F=E8=A1=8C=E4=B8=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nanri/aiimage/config/OssProperties.java | 1 + .../file/service/oss/OssStorageService.java | 32 +++++++++++++++-- .../src/main/resources/application.yml | 1 + .../service/oss/OssStorageServiceTest.java | 36 ++++++++++++++++++- 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/backend-java/src/main/java/com/nanri/aiimage/config/OssProperties.java b/backend-java/src/main/java/com/nanri/aiimage/config/OssProperties.java index 938c6539..5bc875f8 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/config/OssProperties.java +++ b/backend-java/src/main/java/com/nanri/aiimage/config/OssProperties.java @@ -9,6 +9,7 @@ public class OssProperties { private String region; private String endpoint; private String publicEndpoint; + private String downloadEndpoint; private String bucket; private String imageVideoBucket; private String digitalHumanBucket; diff --git a/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/oss/OssStorageService.java b/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/oss/OssStorageService.java index 2c70375f..a3e538a2 100644 --- a/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/oss/OssStorageService.java +++ b/backend-java/src/main/java/com/nanri/aiimage/modules/file/service/oss/OssStorageService.java @@ -302,7 +302,11 @@ public class OssStorageService { } public String generateFreshDownloadUrl(String value) { - return getPublicUrl(value); + StorageLocation location = resolveStorageLocation(value); + if (location == null) { + return getPublicUrl(value); + } + return buildDownloadPublicUrl(location.objectKey(), location.bucket()); } private void uploadFile(File file, String bucket, String objectKey) { @@ -321,6 +325,22 @@ public class OssStorageService { } private String getPublicUrl(String objectKey, String bucket) { + return buildPublicUrl(trimTrailingSlash(publicEndpoint()), objectKey, bucket); + } + + /** + * 结果文件(objectKey 以 {@code result/} 开头)的下载直链。 + * 配置了 download-endpoint 时走下载域名(独立出口/缓存节点),否则回退到公开 endpoint。 + */ + private String buildDownloadPublicUrl(String objectKey, String bucket) { + String base = downloadEndpoint(); + if (base == null || objectKey == null || !objectKey.startsWith("result/")) { + return getPublicUrl(objectKey, bucket); + } + return buildPublicUrl(base, objectKey, bucket); + } + + private String buildPublicUrl(String baseUrl, String objectKey, String bucket) { String encodedPath; try { encodedPath = new URI(null, null, "/" + bucket + "/" + trimLeadingSlash(objectKey), null) @@ -328,7 +348,7 @@ public class OssStorageService { } catch (Exception ex) { encodedPath = "/" + bucket + "/" + trimLeadingSlash(objectKey).replace(" ", "%20"); } - return trimTrailingSlash(publicEndpoint()) + encodedPath; + return trimTrailingSlash(baseUrl) + encodedPath; } private StorageLocation resolveStorageLocation(String value) { @@ -488,6 +508,14 @@ public class OssStorageService { return withScheme(firstNonBlank(ossProperties.getPublicEndpoint(), ossProperties.getEndpoint())); } + private String downloadEndpoint() { + String configured = ossProperties.getDownloadEndpoint(); + if (configured == null || configured.isBlank()) { + return null; + } + return withScheme(configured.trim()); + } + private String resultObjectKey(File file, String moduleType) { return String.format("result/%s/%s/%s", normalizeModuleType(moduleType), UUID.randomUUID(), file.getName()); } diff --git a/backend-java/src/main/resources/application.yml b/backend-java/src/main/resources/application.yml index e81e9833..68efcba3 100644 --- a/backend-java/src/main/resources/application.yml +++ b/backend-java/src/main/resources/application.yml @@ -97,6 +97,7 @@ aiimage: region: ${AIIMAGE_OSS_REGION:us-east-1} endpoint: ${AIIMAGE_OSS_ENDPOINT:https://oss.aishufu.top} public-endpoint: ${AIIMAGE_OSS_PUBLIC_ENDPOINT:https://oss.aishufu.top} + download-endpoint: ${AIIMAGE_OSS_DOWNLOAD_ENDPOINT:} bucket: ${AIIMAGE_OSS_BUCKET:nanri-ai-images} image-video-bucket: ${AIIMAGE_IMAGE_VIDEO_OSS_BUCKET:shufu-video} digital-human-bucket: ${AIIMAGE_DIGITAL_HUMAN_OSS_BUCKET:nanri-ai-digital-human} diff --git a/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/OssStorageServiceTest.java b/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/OssStorageServiceTest.java index a0a3f1f1..b16c6723 100644 --- a/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/OssStorageServiceTest.java +++ b/backend-java/src/test/java/com/nanri/aiimage/modules/file/service/oss/OssStorageServiceTest.java @@ -12,10 +12,11 @@ import static org.junit.jupiter.api.Assertions.assertNull; class OssStorageServiceTest { private OssStorageService storageService; + private OssProperties properties; @BeforeEach void setUp() { - OssProperties properties = new OssProperties(); + properties = new OssProperties(); properties.setEndpoint("https://oss.aishufu.top"); properties.setPublicEndpoint("https://oss.aishufu.top"); properties.setBucket("nanri-ai-images"); @@ -93,4 +94,37 @@ class OssStorageServiceTest { assertNull(storageService.normalizeManagedPublicUrl(null)); assertEquals(" ", storageService.normalizeManagedPublicUrl(" ")); } + + @Test + void resultFilesUseDownloadEndpointWhenConfigured() { + properties().setDownloadEndpoint("https://download.aishufu.top"); + assertEquals( + "https://download.aishufu.top/nanri-ai-images/result/similar_asin/1/%E9%87%8D%E6%96%B0-result.xlsx", + storageService.generateFreshDownloadUrl("result/similar_asin/1/重新-result.xlsx")); + assertEquals( + "https://download.aishufu.top/nanri-ai-images/result/similar_asin/1/re.xlsx", + storageService.generateFreshDownloadUrl("https://oss.aishufu.top/nanri-ai-images/result/similar_asin/1/re.xlsx")); + } + + @Test + void nonResultAndUnmanagedValuesKeepPublicEndpoint() { + properties().setDownloadEndpoint("https://download.aishufu.top"); + assertEquals( + "https://oss.aishufu.top/nanri-ai-images/supply_images/main.jpg", + storageService.generateFreshDownloadUrl("supply_images/main.jpg")); + assertEquals( + "https://download.aishufu.top/shufu-video/result/image_video/demo.mp4", + storageService.generateFreshDownloadUrl("shufu-video/result/image_video/demo.mp4")); + } + + @Test + void downloadEndpointDisabledFallsBackToPublicEndpoint() { + assertEquals( + "https://oss.aishufu.top/nanri-ai-images/result/similar_asin/1/re.xlsx", + storageService.generateFreshDownloadUrl("result/similar_asin/1/re.xlsx")); + } + + private OssProperties properties() { + return properties; + } }