ObjectOperations.kt

package io.github.lishangbu.avalon.s3.facade

import software.amazon.awssdk.core.ResponseBytes
import software.amazon.awssdk.core.ResponseInputStream
import software.amazon.awssdk.core.sync.RequestBody
import software.amazon.awssdk.core.sync.ResponseTransformer
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.CopyObjectRequest
import software.amazon.awssdk.services.s3.model.CopyObjectResponse
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse
import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.s3.model.GetObjectResponse
import software.amazon.awssdk.services.s3.model.HeadObjectRequest
import software.amazon.awssdk.services.s3.model.HeadObjectResponse
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response
import software.amazon.awssdk.services.s3.model.PutObjectRequest
import software.amazon.awssdk.services.s3.model.PutObjectResponse
import java.io.InputStream

/** 对象操作 facade。 */
class ObjectOperations(
    private val s3Client: S3Client,
    private val bucketNameResolver: (String) -> String,
) {
    /** 透传原生 putObject。 */
    fun put(
        request: PutObjectRequest,
        requestBody: RequestBody,
    ): PutObjectResponse = s3Client.putObject(request, requestBody)

    /** 上传字节数组对象。 */
    fun put(
        bucketName: String,
        key: String,
        content: ByteArray,
        contentType: String = "application/octet-stream",
    ): PutObjectResponse =
        put(
            PutObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .contentType(contentType)
                .contentLength(content.size.toLong())
                .build(),
            RequestBody.fromBytes(content),
        )

    /** 上传输入流对象。 */
    fun put(
        bucketName: String,
        key: String,
        stream: InputStream,
        contentLength: Long,
        contentType: String = "application/octet-stream",
    ): PutObjectResponse =
        put(
            PutObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .contentType(contentType)
                .contentLength(contentLength)
                .build(),
            RequestBody.fromInputStream(stream, contentLength),
        )

    /** 透传原生 getObject 输入流。 */
    fun get(request: GetObjectRequest): ResponseInputStream<GetObjectResponse> = s3Client.getObject(request, ResponseTransformer.toInputStream())

    /** 下载为输入流。 */
    fun get(
        bucketName: String,
        key: String,
    ): ResponseInputStream<GetObjectResponse> =
        get(
            GetObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .build(),
        )

    /** 下载为字节数组。 */
    fun getBytes(
        bucketName: String,
        key: String,
    ): ResponseBytes<GetObjectResponse> =
        s3Client.getObjectAsBytes(
            GetObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .build(),
        )

    /** 查询对象元数据。 */
    fun head(request: HeadObjectRequest): HeadObjectResponse = s3Client.headObject(request)

    /** 查询对象元数据。 */
    fun head(
        bucketName: String,
        key: String,
    ): HeadObjectResponse =
        head(
            HeadObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .build(),
        )

    /** 删除对象。 */
    fun delete(request: DeleteObjectRequest): DeleteObjectResponse = s3Client.deleteObject(request)

    /** 删除对象。 */
    fun delete(
        bucketName: String,
        key: String,
    ): DeleteObjectResponse =
        delete(
            DeleteObjectRequest
                .builder()
                .bucket(resolveBucket(bucketName))
                .key(key)
                .build(),
        )

    /** 列举对象。 */
    fun list(request: ListObjectsV2Request): ListObjectsV2Response = s3Client.listObjectsV2(request)

    /** 按前缀列举对象。 */
    fun list(
        bucketName: String,
        prefix: String? = null,
    ): ListObjectsV2Response =
        list(
            ListObjectsV2Request
                .builder()
                .bucket(resolveBucket(bucketName))
                .prefix(prefix)
                .build(),
        )

    /** 复制对象。 */
    fun copy(request: CopyObjectRequest): CopyObjectResponse = s3Client.copyObject(request)

    /** 按桶和 key 复制对象。 */
    fun copy(
        sourceBucketName: String,
        sourceKey: String,
        destinationBucketName: String,
        destinationKey: String,
    ): CopyObjectResponse =
        copy(
            CopyObjectRequest
                .builder()
                .sourceBucket(resolveBucket(sourceBucketName))
                .sourceKey(sourceKey)
                .destinationBucket(resolveBucket(destinationBucketName))
                .destinationKey(destinationKey)
                .build(),
        )

    private fun resolveBucket(bucketName: String): String = bucketNameResolver(bucketName)
}