Appearance
配置 S3 / MinIO
avalon-s3-spring-boot-starter 提供 Avalon 服务端统一的对象存储接入能力,支持:
- Amazon S3
- MinIO
- RustFS
- 阿里云 OSS(S3 兼容)
- 七牛云 Kodo(S3 兼容)
- 通用 S3 兼容实现
- 多命名 client
- 预签名 URL
- Multipart 上传
S3TransferManager- 原生
S3Client与S3ControlClient
依赖
Maven:
xml
<dependency>
<groupId>io.github.lishangbu</groupId>
<artifactId>avalon-s3-spring-boot-starter</artifactId>
<version>${avalon.version}</version>
</dependency>Gradle Kotlin DSL:
kotlin
implementation("io.github.lishangbu:avalon-s3-spring-boot-starter:${avalonVersion}")基础配置
配置前缀是 avalon.s3:
yaml
avalon:
s3:
enabled: true
default-client-name: default
clients:
default:
provider: AWS
region: ap-southeast-1最重要的几个点:
enabled默认是falsedefault-client-name默认是defaultclients至少需要一个启用中的 client- 默认 bean 都来自
default-client-name指向的那个 client
支持的 provider
当前支持六种 provider:
AWSMINIORUSTFSALIYUN_OSSQINIU_KODOGENERIC_S3
默认行为:
AWS默认使用 virtual-hosted 风格,不自动强制path-style-accessMINIO、RUSTFS和GENERIC_S3默认启用path-style-accessALIYUN_OSS和QINIU_KODO默认使用 virtual-hosted 风格AWS在未显式配置region时,会交给 AWS SDK 自己解析MINIO、RUSTFS和GENERIC_S3在未显式配置region时,会回退到us-east-1ALIYUN_OSS和QINIU_KODO要求显式配置region- 除
AWS外,其他 provider 都要求显式配置endpoint
常用属性
yaml
avalon:
s3:
enabled: true
default-client-name: media
clients:
media:
enabled: true
provider: AWS
region: ap-southeast-1
endpoint:
use-arn-region-enabled: true
dualstack-enabled: false
accelerate-mode-enabled: false
path-style-access:
chunked-encoding-enabled:
checksum-validation-enabled:
bucket-aliases:
public: my-public-bucket
private: my-private-bucket
credentials:
access-key-id:
secret-access-key:
session-token:
overrides:
api-call-timeout: 30s
api-call-attempt-timeout: 10s
http:
connection-timeout: 3s
socket-timeout: 30s
read-timeout: 30s
write-timeout: 30s
max-connections: 200
max-concurrency: 200
transfer:
multipart-enabled: true说明:
bucket-aliases用来把业务里使用的别名映射成真实 bucket 名称credentials留空时,默认走 AWS SDK 的DefaultCredentialsProvideraccess-key-id和secret-access-key同时存在时,使用静态凭证session-token也存在时,使用临时会话凭证
AWS 示例
如果部署在 EC2、ECS、EKS、Lambda,推荐直接使用默认凭证链,不显式写 AK / SK:
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: AWS
region: ap-southeast-1
bucket-aliases:
media: my-media-bucket
archive: my-archive-bucket这时会由 AWS SDK 自动按顺序尝试环境变量、profile、web identity、instance role 等凭证来源。
如果必须显式写静态凭证:
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: AWS
region: ap-southeast-1
credentials:
access-key-id: ${AWS_ACCESS_KEY_ID}
secret-access-key: ${AWS_SECRET_ACCESS_KEY}MinIO 示例
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: MINIO
endpoint: http://127.0.0.1:9000
region: us-east-1
path-style-access: true
credentials:
access-key-id: minioadmin
secret-access-key: minioadmin
bucket-aliases:
media: avalon-media本地开发时通常只需要这几个属性:
provider=MINIOendpointpath-style-access=true- 静态凭证
RustFS 示例
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: RUSTFS
endpoint: http://127.0.0.1:9000
region: us-east-1
credentials:
access-key-id: rustfsadmin
secret-access-key: rustfsadmin
bucket-aliases:
media: avalon-media说明:
RUSTFS默认按 path-style 访问- 如果你使用自定义网关域名,也可以显式覆写
path-style-access
阿里云 OSS 示例
阿里云 OSS 的 S3 兼容接入,通常需要同时配置 Region ID 和 S3 兼容 endpoint:
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: ALIYUN_OSS
endpoint: https://oss-cn-hangzhou.aliyuncs.com
region: cn-hangzhou
credentials:
access-key-id: ${ALIYUN_ACCESS_KEY_ID}
secret-access-key: ${ALIYUN_ACCESS_KEY_SECRET}
bucket-aliases:
media: my-oss-bucket说明:
endpoint形如https://oss-<region>.aliyuncs.comregion应填写 Region ID,例如cn-hangzhouALIYUN_OSS默认使用 virtual-hosted 风格- 如有特殊网关要求,也可以显式设置
path-style-access: true
七牛云 Kodo 示例
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: QINIU_KODO
endpoint: https://s3.cn-east-1.qiniucs.com
region: cn-east-1
credentials:
access-key-id: ${QINIU_ACCESS_KEY}
secret-access-key: ${QINIU_SECRET_KEY}
bucket-aliases:
media: my-kodo-bucket说明:
endpoint使用七牛提供的 S3 服务域名region需要与 endpoint 所在区域保持一致QINIU_KODO默认使用 virtual-hosted 风格- 如果你的 bucket 命名或网关策略更适合 path-style,也可以显式开启
path-style-access
多 client 配置
如果一个应用要同时连接多个对象存储,可以直接配置多个命名 client:
yaml
avalon:
s3:
enabled: true
default-client-name: media
clients:
media:
provider: AWS
region: ap-southeast-1
bucket-aliases:
public: media-public-bucket
archive:
provider: AWS
region: ap-southeast-1
bucket-aliases:
cold: archive-cold-bucket
local:
provider: MINIO
endpoint: http://127.0.0.1:9000
region: us-east-1
path-style-access: true
credentials:
access-key-id: minioadmin
secret-access-key: minioadmin
rustfs:
provider: RUSTFS
endpoint: http://127.0.0.1:9001
region: us-east-1
credentials:
access-key-id: rustfsadmin
secret-access-key: rustfsadmin
oss:
provider: ALIYUN_OSS
endpoint: https://oss-cn-hangzhou.aliyuncs.com
region: cn-hangzhou
credentials:
access-key-id: ${ALIYUN_ACCESS_KEY_ID}
secret-access-key: ${ALIYUN_ACCESS_KEY_SECRET}
qiniu:
provider: QINIU_KODO
endpoint: https://s3.cn-east-1.qiniucs.com
region: cn-east-1
credentials:
access-key-id: ${QINIU_ACCESS_KEY}
secret-access-key: ${QINIU_SECRET_KEY}说明:
- Spring 默认只暴露
default-client-name对应的一组 bean - 其他命名 client 通过
AvalonS3ClientRegistry获取
可直接注入的 Bean
启用 starter 后,默认 client 会自动注册以下 Bean:
S3FacadeBucketOperationsObjectOperationsMultipartOperationsPresignOperationsTransferOperationsS3ClientS3AsyncClientS3WaiterS3PresignerS3ControlClientS3ControlAsyncClientS3TransferManagerAvalonS3ClientRegistry
S3Facade 是默认入口,聚合了常用 facade 和原生 client。
使用 S3Facade
kotlin
import io.github.lishangbu.avalon.s3.facade.S3Facade
import org.springframework.stereotype.Service
import java.time.Duration
@Service
class FileStorageService(
private val s3Facade: S3Facade,
) {
fun upload(
key: String,
content: ByteArray,
) {
s3Facade.objects.put("media", key, content, "application/octet-stream")
}
fun downloadUrl(key: String): String =
s3Facade.presign
.get("media", key, Duration.ofMinutes(5))
.url()
.toString()
}这里的 "media" 会先按 bucket-aliases 解析,再转换成真实 bucket 名称。
使用命名 client 注册表
kotlin
import io.github.lishangbu.avalon.s3.client.AvalonS3ClientRegistry
import org.springframework.stereotype.Service
@Service
class CrossStorageService(
private val registry: AvalonS3ClientRegistry,
) {
fun copyToArchive(key: String) {
val media = registry.facade("media")
val archive = registry.facade("archive")
val content = media.objects.getBytes("public", key).asByteArray()
archive.objects.put("cold", key, content)
}
}使用原生 S3Client
如果 facade 还没有覆盖某个能力,直接使用 AWS SDK 即可:
kotlin
import org.springframework.stereotype.Service
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.GetBucketVersioningRequest
@Service
class VersioningQueryService(
private val s3Client: S3Client,
) {
fun getStatus(bucket: String) =
s3Client.getBucketVersioning(
GetBucketVersioningRequest
.builder()
.bucket(bucket)
.build(),
)
}这也是当前 starter 的设计重点:
- facade 负责常用能力
- 原生 SDK 负责完整能力
使用 S3ControlClient
Amazon S3 的一部分高级控制面能力并不在 S3Client 上,而是在 S3ControlClient 上,例如:
- Access Points
- Batch Operations
- Storage Lens
- Multi-Region Access Points
- 其他 S3 Control API
示例:
kotlin
import org.springframework.stereotype.Service
import software.amazon.awssdk.services.s3control.S3ControlClient
import software.amazon.awssdk.services.s3control.model.ListAccessPointsRequest
@Service
class AccessPointService(
private val s3ControlClient: S3ControlClient,
) {
fun list(accountId: String) =
s3ControlClient.listAccessPoints(
ListAccessPointsRequest
.builder()
.accountId(accountId)
.build(),
)
}注意:
S3ControlClient主要面向 Amazon S3- 对 MinIO 或其他兼容实现,很多控制面操作并不支持
常用 facade 能力
1. 桶操作
kotlin
s3Facade.buckets.create("media")
s3Facade.buckets.exists("media")
s3Facade.buckets.list()
s3Facade.buckets.delete("media")2. 对象操作
kotlin
s3Facade.objects.put("media", "hello.txt", "hello".toByteArray(), "text/plain")
s3Facade.objects.getBytes("media", "hello.txt")
s3Facade.objects.head("media", "hello.txt")
s3Facade.objects.list("media", "prefix/")
s3Facade.objects.delete("media", "hello.txt")3. Multipart 上传
kotlin
val upload = s3Facade.multipart.create("media", "large.bin")
val part1 = s3Facade.multipart.uploadPart("media", "large.bin", upload.uploadId(), 1, firstChunk)
val part2 = s3Facade.multipart.uploadPart("media", "large.bin", upload.uploadId(), 2, secondChunk)
s3Facade.multipart.complete(
bucketName = "media",
key = "large.bin",
uploadId = upload.uploadId(),
completedParts = listOf(
CompletedPart.builder().partNumber(1).eTag(part1.eTag()).build(),
CompletedPart.builder().partNumber(2).eTag(part2.eTag()).build(),
),
)注意:
- 除最后一个 part 外,Amazon S3 要求每个 part 至少
5 MiB - 这条规则在 MinIO 兼容实现上通常也会生效
4. 预签名 URL
kotlin
val getUrl =
s3Facade.presign
.get("media", "hello.txt", Duration.ofMinutes(5))
.url()
val putUrl =
s3Facade.presign
.put("media", "upload.txt", Duration.ofMinutes(10), "text/plain")
.url()支持:
GETPUTDELETEHEADUploadPart
5. 传输管理
kotlin
import java.nio.file.Paths
val upload =
s3Facade.transfer.uploadFile(
bucketName = "media",
key = "avatars/u1.png",
source = Paths.get("/data/avatar.png"),
contentType = "image/png",
)
s3Facade.transfer.await(upload)目录上传和下载也可以直接使用 TransferOperations。
自定义 SDK Builder
如果你要接入代理、链路追踪、特殊 endpoint、额外 override 配置,可以注册以下扩展点之一:
S3ClientBuilderCustomizerS3AsyncClientBuilderCustomizerS3PresignerBuilderCustomizerS3ControlClientBuilderCustomizerS3ControlAsyncClientBuilderCustomizerS3TransferManagerBuilderCustomizer
示例:
kotlin
import io.github.lishangbu.avalon.s3.customizer.S3ClientBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.net.URI
@Configuration
class S3CustomizationConfiguration {
@Bean
fun s3ClientBuilderCustomizer(): S3ClientBuilderCustomizer =
S3ClientBuilderCustomizer { clientName, properties, builder ->
if (clientName == "media") {
builder.endpointOverride(URI.create("https://s3.ap-southeast-1.amazonaws.com"))
}
}
}适用场景:
- 统一注入自定义 User-Agent
- 增加 SDK override 配置
- 做某个命名 client 的定制化接入
与旧版本的差异
当前版本已经不再提供旧的 S3Template 和旧的 s3.* 配置前缀。
现在应该改成:
- 使用
avalon.s3.*配置 - 使用
S3Facade或AvalonS3ClientRegistry - 对高级能力直接使用
S3Client/S3ControlClient
旧版本常见写法:
yaml
s3:
endpoint: http://127.0.0.1:9000
access-key: xxx
secret-key: xxx现在应改成:
yaml
avalon:
s3:
enabled: true
clients:
default:
provider: MINIO
endpoint: http://127.0.0.1:9000
region: us-east-1
path-style-access: true
credentials:
access-key-id: xxx
secret-access-key: xxx当前边界
当前 starter 已经把原生 AWS S3 与 S3 Control client 暴露出来,但 facade 仍然只覆盖最常用的能力域:
- bucket
- object
- multipart
- presign
- transfer
如果你需要 Access Points、Batch Operations、Storage Lens、Object Lock、Replication、Inventory 等能力,推荐直接使用原生 AWS SDK request / response 对象调用。