Nuñito Calzada
Nuñito Calzada

Reputation: 2056

The bucket you are attempting to access must be addressed using the specified endpoint Uploading a file to AWS S3

I am trying to upload a file to S3 using this example:

https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/s3/src/main/java/com/example/s3/PutObject.java

But I have this error:

The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint. (Service: S3, Status Code: 301, Request ID: 15F45F10B11A3267, Extended Request ID: 9TdN5/GMl9h6VGPq6ee4MttjlLFm+V5GjIaXIJLAO1YasdZCls0M4udxJso8n1P+qMr4iigP5g=)

Upvotes: 1

Views: 790

Answers (2)

Guss
Guss

Reputation: 32315

What annoyed me is that if you create the S3 client once during initialization and re-use it (which is just the thing that makes sense), then you have to know ahead of time all the regions where you need to access buckets, and pre-init multiple S3 clients - there's no way that I can find (at least for the Java SDK) to "just send one request using a different endpoint".

Because pre-initing all the clients for all the regions that are possible (and they keep adding more all the time) is just insane, I wrote up this wrapper (it is using the async clients and exposes a Vert.x promises API, but it should be easy to adapt to anything else):

/**
 * S3 client factory that helps resolve region-specific clients
 * Licensed for any use under CC0
 */
public class S3AsyncClientFactory {
    private ConcurrentHashMap<String, S3AsyncClient> cache = 
            new ConcurrentHashMap<>();
    private S3AsyncClient global;
        
    private S3AsyncClientFactory() {
    // I call it "global" but it actually uses your "default region".
    // There is a "global" region, but I couldn't be bothered to
    // figure out how to create such a client.
        global = S3AsyncClient.create();
    }
        
    public Future<Region> getBucketLocation(String bucket) {
        return Future.fromCompletionStage(
                global.getBucketLocation(b -> b.bucket(bucket)))
                .map(loc -> loc.locationConstraintAsString())
            // annoyingly, get location returns an empty string for
            // the "standard region" and Region.of() can't handle that
                .map(loc -> loc == null || loc.isEmpty() ?
                        Region.US_EAST_1 : Region.of(loc));
    }
        
    public Future<S3AsyncClient> forBucket(String bucket) {
        return getBucketLocation(bucket).map(this::forRegion);
    }
    
    public S3AsyncClient global() {
        return global;
    }
        
    public S3AsyncClient forRegion(Region region) {
        return cache.computeIfAbsent(region.toString(),
            // you may want to replace this part if you have custom
            // init code or you need non-default credentials chain
                r -> S3AsyncClient.builder().region(region).build());
    }
        
    public S3AsyncClient forRegion(String region) {
        return forRegion(Region.of(region));
    }
}

With that piece of code you just create and cache per-region S3 clients, as needed:

factory.forBucket(bucket).compose(c -> Future.fromCompletionStage(
        c.getObject(b -> b.bucket(bucket).key(key), responseTransformer)))
        .map(res -> ...

(please don't mind the ugly "different libraries have difference concurrency implementations" code 🤷)

You may also want to have a bucket-keyed client implementation cache (I have it in another layer where it makes more sense for my use case).

Upvotes: 0

Abdul Moeez
Abdul Moeez

Reputation: 1401

It seems likely that this bucket was created in a different region. Please send your request to your region endpoint. The default region is as follow's

US Standard is us-east-1

The upper link have region 'Region.US_WEST_2'. First confirm your region then send your request accordingly.

Upvotes: 2

Related Questions