## Customization

### Customize Object Key Path

You can customize your key path by defining a prefix resolver:

<BlockSwitcher>
<Block name="Java">

```java
// Define your own prefix resolver, that conforms to `AWSS3StoragePluginPrefixResolver`
public class MyDeveloperPrefixResolver implements AWSS3PluginPrefixResolver {

    @Override
    public void resolvePrefix(@NonNull StorageAccessLevel accessLevel, @Nullable String targetIdentity,
                              @NonNull Consumer<String> onSuccess, @NonNull Consumer<StorageException> onError) {
        switch (accessLevel) {
            case PUBLIC: {
                onSuccess.accept("myPublicPrefix/");
            }
            case PROTECTED: {
                onSuccess.accept("myProtectedPrefix/" + targetIdentity + "/");
            }
            case PRIVATE: {
                onSuccess.accept("myPrivatePrefix/" + targetIdentity + "/");
            }
        }
    }
}
```

</Block>
<Block name="Kotlin">

```kotlin
// Define your own prefix resolver, that conforms to `AWSS3StoragePluginPrefixResolver`
class MyDeveloperPrefixResolver: AWSS3PluginPrefixResolver {

    override fun resolvePrefix(
        accessLevel: StorageAccessLevel,
        targetIdentity: String?,
        onSuccess: Consumer<String>,
        onError: Consumer<StorageException>
    ) {
        when(accessLevel) {
            StorageAccessLevel.PUBLIC -> {
                onSuccess.accept("myPublicPrefix/")
            }
            StorageAccessLevel.PROTECTED -> {
                onSuccess.accept("myProtectedPrefix/$targetIdentity/")
            }
            StorageAccessLevel.PRIVATE -> {
                onSuccess.accept("myPrivatePrefix/$targetIdentity/")
            }
        }
    }
}
```

</Block>
</BlockSwitcher>

Then configure the storage plugin with the resolver.

<BlockSwitcher>
<Block name="Java">

```java
AWSS3StoragePluginConfiguration.Builder builder = new AWSS3StoragePluginConfiguration.Builder();
builder.setAwsS3PluginPrefixResolver(new MyDeveloperPrefixResolver());
Amplify.addPlugin(new AWSS3StoragePlugin(builder.build()));
```

</Block>
<Block name="Kotlin">

```kotlin
Amplify.addPlugin<Plugin<*>>(
    AWSS3StoragePlugin(
        AWSS3StoragePluginConfiguration {
            awsS3PluginPrefixResolver = MyDeveloperPrefixResolver()
        }
    )
)
```

</Block>
</BlockSwitcher>

Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path *myPublicPrefix/*:
```xml
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "s3:GetObject",
            "s3:PutObject",
            "s3:DeleteObject"
        ],
        "Resource": [
            "arn:aws:s3:::your-s3-bucket/myPublicPrefix/*",
        ]
    }
]
```
If you want to have custom *private* path prefix like *myPrivatePrefix/*, you need to add it into your IAM policy:
```xml
"Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "s3:GetObject",
            "s3:PutObject",
            "s3:DeleteObject"
        ],
        "Resource": [
            "arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*"
        ]
    }
]
```
#### Passthrough PrefixResolver

If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, create a prefix resolver that returns an empty string:

<BlockSwitcher>
<Block name="Java">

```java
public class PassThroughPrefixResolver implements AWSS3PluginPrefixResolver {

    @Override
    public void resolvePrefix(@NonNull StorageAccessLevel accessLevel, @Nullable String targetIdentity,
                              @NonNull Consumer<String> onSuccess, @NonNull Consumer<StorageException> onError) {
        onSuccess.accept("")
    }
}
```

</Block>
<Block name="Kotlin">

```kotlin
class PassThroughPrefixResolver: AWSS3PluginPrefixResolver {

    override fun resolvePrefix(
        accessLevel: StorageAccessLevel,
        targetIdentity: String?,
        onSuccess: Consumer<String>,
        onError: Consumer<StorageException>
    ) {
        onSuccess.accept("")
    }
}
```

</Block>
</BlockSwitcher>
