Работа с S3 и облачными хранилищами в Kotlin: AWS SDK и Ktor S3 Client

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с S3 и облачными хранилищами в Kotlin

В современной разработке на Kotlin часто возникает необходимость интеграции с облачными хранилищами, совместимыми с протоколом Amazon S3 (Simple Storage Service). Это могут быть AWS S3, MinIO, DigitalOcean Spaces, Yandex Object Storage и другие. В этой статье мы рассмотрим два основных подхода: использование официального AWS SDK для Kotlin и легковесного клиента на основе Ktor.

1. AWS SDK для Kotlin (aws-sdk-kotlin)

Официальный SDK от Amazon предоставляет типизированный и асинхронный API, полностью поддерживающий корутины Kotlin. Это наиболее полное решение для работы с S3.

Подключение зависимости

Добавьте в build.gradle.kts:

dependencies {
    implementation("aws.sdk.kotlin:s3:1.0.0")
    implementation("aws.smithy.kotlin:http-client-engine-ktor:1.0.0")
}

Инициализация клиента

import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.model.*

suspend fun createClient(): S3Client = S3Client.fromEnvironment {
    region = "ru-central1"
    // Для MinIO или других S3-совместимых хранилищ:
    endpointUrl = "https://storage.yandexcloud.net"
    // Настройка через переменные окружения AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY
}

Загрузка файла (PutObject)

suspend fun uploadFile(client: S3Client, bucket: String, key: String, filePath: String) {
    val fileBytes = java.io.File(filePath).readBytes()
    client.putObject(
        PutObjectRequest {
            this.bucket = bucket
            this.key = key
            this.body = fileBytes.toByteArray()
        }
    )
    println("Файл $key загружен в бакет $bucket")
}

Скачивание файла (GetObject)

suspend fun downloadFile(client: S3Client, bucket: String, key: String, savePath: String) {
    val response = client.getObject(
        GetObjectRequest {
            this.bucket = bucket
            this.key = key
        }
    )
    val bytes = response.body?.readAll()
    if (bytes != null) {
        java.io.File(savePath).writeBytes(bytes)
        println("Файл $key сохранён в $savePath")
    }
}

Список объектов в бакете

suspend fun listObjects(client: S3Client, bucket: String): List<String> {
    val response = client.listObjectsV2(
        ListObjectsV2Request {
            this.bucket = bucket
        }
    )
    return response.contents?.map { it.key ?: "" } ?: emptyList()
}

2. Ktor S3 Client (легковесная альтернатива)

Если вам не нужна вся мощь AWS SDK, можно использовать Ktor вместе с библиотекой ktor-client-s3. Это даёт минимальный вес и полный контроль над HTTP-запросами.

Подключение

dependencies {
    implementation("io.ktor:ktor-client-core:2.3.0")
    implementation("io.ktor:ktor-client-cio:2.3.0")
    implementation("io.ktor:ktor-client-content-negotiation:2.3.0")
    implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.0")
    // Для подписи запросов S3
    implementation("io.ktor:ktor-client-auth:2.3.0")
}

Создание S3-клиента на Ktor

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.utils.io.*

class S3KtorClient(
    private val endpoint: String,
    private val accessKey: String,
    private val secretKey: String
) {
    private val client = HttpClient {
        install(io.ktor.client.plugins.auth.Auth) {
            basic {
                credentials {
                    username = accessKey
                    password = secretKey
                }
            }
        }
    }

    suspend fun putObject(bucket: String, key: String, data: ByteArray) {
        client.put("$endpoint/$bucket/$key") {
            setBody(ByteReadChannel(data))
            contentType(ContentType.Application.OctetStream)
        }
    }

    suspend fun getObject(bucket: String, key: String): ByteArray {
        val response = client.get("$endpoint/$bucket/$key")
        return response.bodyAsChannel().readRemaining().readBytes()
    }

    suspend fun deleteObject(bucket: String, key: String) {
        client.delete("$endpoint/$bucket/$key")
    }
}

Пример использования

suspend fun main() {
    val s3 = S3KtorClient(
        endpoint = "https://storage.yandexcloud.net",
        accessKey = System.getenv("ACCESS_KEY"),
        secretKey = System.getenv("SECRET_KEY")
    )

    // Загрузка
    s3.putObject("my-bucket", "docs/report                    

Рекомендаций