You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TestContainers与LocalStack S3:S3 URI解析失败,端点IP不被认可

问题描述

我通过以下代码获取AWS S3对象URL:

s3AsyncClient.utilities()
        .getUrl(r -> r.region(awsRegion).bucket(bucketName).key(key));

在使用TestContainers + LocalStack S3做集成测试时,返回的URL为:

http://127.0.0.1:51435/my-bucket/games/123.json

但尝试将其转换为S3Uri时:

S3Uri s3Uri = s3AsyncClient.utilities().parseUri(url.toURI());

抛出错误:

java.lang.IllegalArgumentException: Invalid S3 URI: hostname does not appear to be a valid S3 endpoint: http://127.0.0.1:51435/my-bucket/games/123.json

AWS解析器不认可127.0.0.1为有效S3端点,当前LocalStack配置如下:

private static final LocalStackContainer LOCAL_STACK_CONTAINER = new LocalStackContainer(LOCAL_STACK_IMAGE)
        .withServices(LocalStackContainer.Service.S3);

static {
    LOCAL_STACK_CONTAINER.start();
}

@DynamicPropertySource
static void s3Properties(final DynamicPropertyRegistry registry) {
    registry.add("ssp.s3.region", LOCAL_STACK_CONTAINER::getRegion);
    registry.add("ssp.s3.endpoint", () -> LOCAL_STACK_CONTAINER.getEndpointOverride(LocalStackContainer.Service.S3));
}
解决方案

方案一:用LocalStack测试域名生成虚拟主机格式URL

LocalStack提供官方测试域名localhost.localstack.cloud,配置容器使用该域名,并让AWS SDK生成虚拟主机样式URL,AWS解析器会自动识别为合法端点。

修改LocalStack配置

private static final LocalStackContainer LOCAL_STACK_CONTAINER = new LocalStackContainer(LOCAL_STACK_IMAGE)
        .withServices(LocalStackContainer.Service.S3)
        .withEnv("LOCALSTACK_HOSTNAME", "localhost.localstack.cloud");

配置S3AsyncClient启用虚拟主机样式访问

创建客户端时开启虚拟主机模式:

S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
        .region(Region.of(awsRegion))
        .endpointOverride(URI.create(localStackEndpoint))
        .serviceConfiguration(config -> config.virtualHostStyleEnabled(true))
        .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test")))
        .build();

此时生成的URL会是http://my-bucket.localhost.localstack.cloud:51435/games/123.json,调用parseUri即可正常解析。

方案二:手动解析URL构造S3Uri(测试场景快速解决)

如果不想修改容器和客户端配置,可直接绕过AWS SDK的parseUri方法,手动解析URL路径构造S3Uri:

URI uri = url.toURI();
String path = uri.getPath().substring(1); // 移除路径开头的/
int bucketSeparatorIndex = path.indexOf('/');

String bucketName;
String key;
if (bucketSeparatorIndex == -1) {
    bucketName = path;
    key = "";
} else {
    bucketName = path.substring(0, bucketSeparatorIndex);
    key = path.substring(bucketSeparatorIndex + 1);
}

S3Uri s3Uri = S3Uri.builder().bucket(bucketName).key(key).build();

这种方式适合测试场景,无需调整现有容器或客户端配置。

方案三:配置SDK允许自定义端点解析

通过自定义EndpointProvider让AWS SDK认可127.0.0.1为合法S3端点,适合需要严格模拟生产解析逻辑的场景:

S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
        .region(Region.of(awsRegion))
        .endpointOverride(URI.create(localStackEndpoint))
        .endpointProvider((endpointParams, context) -> CompletableFuture.completedFuture(
                Endpoint.builder()
                        .uri(URI.create(localStackEndpoint))
                        .build()
        ))
        .serviceConfiguration(config -> config.pathStyleAccessEnabled(true))
        .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test")))
        .build();

同时,解析时使用S3UriParser的重载方法指定允许的端点:

S3UriParser parser = S3UriParser.create();
S3Uri s3Uri = parser.parseUri(url.toURI(), ParseUriRequest.builder().allowedEndpoints(Set.of(localStackEndpoint)).build());

内容的提问来源于stack exchange,提问作者user471011

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:06:11