Работа с S3 и облачными хранилищами в Rust: aws-sdk-s3
Облачные объектные хранилища, совместимые с Amazon S3, стали стандартом де-факто для хранения файлов, бэкапов, статики и данных. В экосистеме Rust для работы с S3 используется официальный AWS SDK для Rust — aws-sdk-s3. Этот инструмент позволяет не только подключаться к Amazon S3, но и к любым S3-совместимым хранилищам: MinIO, Yandex Object Storage, DigitalOcean Spaces, Cloudflare R2 и другим.
Установка и настройка
Добавьте в Cargo.toml следующие зависимости:
[dependencies]
aws-sdk-s3 = "1.15"
aws-config = "1.1"
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
Для аутентификации можно использовать переменные окружения, файл конфигурации AWS (~/.aws/credentials) или передавать ключи программно. Минимальная инициализация клиента выглядит так:
use aws_sdk_s3::Client;
use aws_config::meta::region::RegionProviderChain;
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let region_provider = RegionProviderChain::default_provider()
.or_else("ru-central1");
let config = aws_config::from_env()
.region(region_provider)
.load()
.await;
let client = Client::new(&config);
// Далее работаем с client
Ok(())
}
Основные операции с S3
Создание бакета
use aws_sdk_s3::model::BucketLocationConstraint;
let bucket_name = "my-rust-bucket";
let constraint = BucketLocationConstraint::from("ru-central1");
let _ = client
.create_bucket()
.bucket(bucket_name)
.create_bucket_configuration(
aws_sdk_s3::model::CreateBucketConfiguration::builder()
.location_constraint(constraint)
.build()
)
.send()
.await?;
Загрузка файла (PutObject)
use aws_sdk_s3::primitives::ByteStream;
use std::path::Path;
let file_path = "local_file.txt";
let body = ByteStream::from_path(Path::new(file_path)).await?;
client
.put_object()
.bucket(bucket_name)
.key("uploads/remote_file.txt")
.body(body)
.send()
.await?;
println!("Файл загружен");
Скачивание файла (GetObject)
use tokio::io::AsyncWriteExt;
let resp = client
.get_object()
.bucket(bucket_name)
.key("uploads/remote_file.txt")
.send()
.await?;
let mut file = tokio::fs::File::create("downloaded.txt").await?;
let mut stream = resp.body;
while let Some(chunk) = stream.try_next().await? {
file.write_all(&chunk).await?;
}
println!("Файл скачан");
Список объектов в бакете
let objects = client
.list_objects_v2()
.bucket(bucket_name)
.prefix("uploads/")
.send()
.await?;
for obj in objects.contents().unwrap_or_default() {
println!("- {}", obj.key().unwrap_or("unknown"));
}
Подключение к MinIO и другим S3-совместимым хранилищам
Для работы с MinIO (или любым другим не-AWS хранилищем) необходимо переопределить endpoint и отключить проверку сигнатуры региона (если требуется). Вот полный пример:
use aws_sdk_s3::{Client, Config, Credentials, Region};
use aws_smithy_client::erase::DynConnector;
use aws_smithy_client::hyper_ext;
use http::Uri;
let creds = Credentials::new(
"minioadmin",
"minioadmin",
None,
None,
"minio",
);
let endpoint = Uri::from_static("http://localhost:9000");
let region = Region::new("us-east-1");
let config = Config::builder()
.credentials_provider(creds)
.region(region)
.endpoint_resolver(endpoint)
.force_path_style(true) // Важно для MinIO
.build();
let client = Client::from_conf(config);
// Все операции с client теперь идут через MinIO
Обратите внимание на force_path_style(true) — многие S3-совместимые хранилища (особенно старые версии MinIO) требуют path-style доступа (bucket в пути URL), а не виртуального хостинга.
Работа с большими файлами (Multipart Upload)
Для файлов размером более 100 МБ рекомендуется использовать multipart upload. В aws-sdk-s3 это реализовано через отдельные шаги:
use aws_sdk_s3::model::{CompletedPart, CompletedMultipartUpload};
// 1