use std::error::Error;
use tinify::{S3Options, StoreOptions, Tinify};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
println!("đž Tinify: S3 Compatible Storage Example");
println!("==========================================");
let api_key = std::env::var("TINIFY_API_KEY")
.unwrap_or_else(|_| "XZmVxmxJxbx4PZbHyxwX74v8N0LLtvqq".to_string());
let client = Tinify::new(api_key)?;
println!("â
Client initialized");
create_test_image("s3_compatible_input.png").await?;
let source = client.source_from_file("s3_compatible_input.png").await?;
println!("â
Source image loaded");
println!("\nđ Example 1: Digital Ocean Spaces");
let do_spaces_options = S3Options {
service: "s3".to_string(),
aws_access_key_id: std::env::var("DO_SPACES_KEY")
.unwrap_or_else(|_| "DEMO_DO_SPACES_KEY".to_string()),
aws_secret_access_key: std::env::var("DO_SPACES_SECRET")
.unwrap_or_else(|_| "DEMO_DO_SPACES_SECRET".to_string()),
region: "nyc3".to_string(), path: "my-space/images/compressed-image.png".to_string(),
headers: None,
acl: Some("public-read".to_string()),
};
match source.store(StoreOptions::S3(do_spaces_options)).await {
Ok(result) => {
println!(" â
Image stored to DigitalOcean Spaces!");
if let Some(count) = result.compression_count() {
println!(" đ Compression count: {}", count);
}
}
Err(e) => {
println!(" â DigitalOcean Spaces error: {}", e);
println!(" âšī¸ This is expected with demo credentials");
}
}
println!("\nđŊ Example 2: Backblaze B2 Cloud Storage");
let b2_options = S3Options {
service: "s3".to_string(),
aws_access_key_id: std::env::var("B2_APPLICATION_KEY_ID")
.unwrap_or_else(|_| "DEMO_B2_KEY_ID".to_string()),
aws_secret_access_key: std::env::var("B2_APPLICATION_KEY")
.unwrap_or_else(|_| "DEMO_B2_APPLICATION_KEY".to_string()),
region: "us-west-002".to_string(), path: "my-bucket/compressed/image.png".to_string(),
headers: None,
acl: None, };
let source2 = client.source_from_file("s3_compatible_input.png").await?;
match source2.store(StoreOptions::S3(b2_options)).await {
Ok(_) => {
println!(" â
Image stored to Backblaze B2!");
}
Err(e) => {
println!(" â Backblaze B2 error: {}", e);
println!(" âšī¸ This is expected with demo credentials");
}
}
println!("\nđļī¸ Example 3: Wasabi Hot Cloud Storage");
let wasabi_options = S3Options {
service: "s3".to_string(),
aws_access_key_id: std::env::var("WASABI_ACCESS_KEY")
.unwrap_or_else(|_| "DEMO_WASABI_ACCESS_KEY".to_string()),
aws_secret_access_key: std::env::var("WASABI_SECRET_KEY")
.unwrap_or_else(|_| "DEMO_WASABI_SECRET_KEY".to_string()),
region: "us-east-1".to_string(), path: "my-bucket/optimized/image.png".to_string(),
headers: None,
acl: Some("public-read".to_string()),
};
let source3 = client.source_from_file("s3_compatible_input.png").await?;
match source3.store(StoreOptions::S3(wasabi_options)).await {
Ok(_) => {
println!(" â
Image stored to Wasabi!");
}
Err(e) => {
println!(" â Wasabi storage error: {}", e);
println!(" âšī¸ This is expected with demo credentials");
}
}
println!("\n⥠Example 4: G-Core Labs Cloud Storage");
let gcore_options = S3Options {
service: "s3".to_string(),
aws_access_key_id: std::env::var("GCORE_ACCESS_KEY")
.unwrap_or_else(|_| "DEMO_GCORE_ACCESS_KEY".to_string()),
aws_secret_access_key: std::env::var("GCORE_SECRET_KEY")
.unwrap_or_else(|_| "DEMO_GCORE_SECRET_KEY".to_string()),
region: "ams".to_string(), path: "my-storage/images/compressed.png".to_string(),
headers: None,
acl: None,
};
let source4 = client.source_from_file("s3_compatible_input.png").await?;
match source4.store(StoreOptions::S3(gcore_options)).await {
Ok(_) => {
println!(" â
Image stored to G-Core Labs!");
}
Err(e) => {
println!(" â G-Core Labs error: {}", e);
println!(" âšī¸ This is expected with demo credentials");
}
}
println!("\nđ Example 5: MinIO (Self-hosted S3)");
let minio_options = S3Options {
service: "s3".to_string(),
aws_access_key_id: std::env::var("MINIO_ACCESS_KEY")
.unwrap_or_else(|_| "DEMO_MINIO_ACCESS_KEY".to_string()),
aws_secret_access_key: std::env::var("MINIO_SECRET_KEY")
.unwrap_or_else(|_| "DEMO_MINIO_SECRET_KEY".to_string()),
region: "us-east-1".to_string(), path: "my-bucket/uploads/processed.png".to_string(),
headers: None,
acl: None,
};
let source5 = client.source_from_file("s3_compatible_input.png").await?;
match source5.store(StoreOptions::S3(minio_options)).await {
Ok(_) => {
println!(" â
Image stored to MinIO!");
}
Err(e) => {
println!(" â MinIO storage error: {}", e);
println!(" âšī¸ This is expected with demo credentials");
}
}
println!("\nđ§ Example 6: Configuration patterns for S3-compatible services");
let services = vec![
(
"DigitalOcean Spaces",
vec![
"DO_SPACES_KEY=your-spaces-access-key",
"DO_SPACES_SECRET=your-spaces-secret-key",
"# Region: nyc3, fra1, sfo3, etc.",
],
),
(
"Backblaze B2",
vec![
"B2_APPLICATION_KEY_ID=your-key-id",
"B2_APPLICATION_KEY=your-application-key",
"# Region: us-west-002, eu-central-003, etc.",
],
),
(
"Wasabi",
vec![
"WASABI_ACCESS_KEY=your-access-key",
"WASABI_SECRET_KEY=your-secret-key",
"# Region: us-east-1, eu-central-1, ap-northeast-1, etc.",
],
),
(
"G-Core Labs",
vec![
"GCORE_ACCESS_KEY=your-access-key",
"GCORE_SECRET_KEY=your-secret-key",
"# Region: ams, fra, sin, etc.",
],
),
(
"MinIO",
vec![
"MINIO_ACCESS_KEY=your-minio-access-key",
"MINIO_SECRET_KEY=your-minio-secret-key",
"# Usually self-hosted with custom endpoint",
],
),
];
for (service, env_vars) in services {
println!(" đĻ {}:", service);
for var in env_vars {
println!(" {}", var);
}
println!();
}
println!("\nđ° Example 7: S3-Compatible Services Comparison");
let comparison = vec![
(
"AWS S3",
"Standard",
"High reliability, global",
"$0.023/GB/month",
),
(
"DigitalOcean Spaces",
"Simple",
"Easy to use, good pricing",
"$0.02/GB/month",
),
(
"Backblaze B2",
"Low-cost",
"Very affordable, pay-per-use",
"$0.005/GB/month",
),
(
"Wasabi",
"Hot storage",
"Unlimited egress, fast",
"$0.0059/GB/month",
),
(
"G-Core Labs",
"Global CDN",
"CDN integrated, fast delivery",
"$0.03/GB/month",
),
(
"MinIO",
"Self-hosted",
"Full control, on-premises",
"Infrastructure costs only",
),
];
println!(" đ Service Comparison:");
println!(" âââââââââââââââââââŦââââââââââââââŦâââââââââââââââââââââââŦâââââââââââââââââââ");
println!(" â Service â Type â Key Feature â Pricing Est. â");
println!(" âââââââââââââââââââŧââââââââââââââŧâââââââââââââââââââââââŧâââââââââââââââââââ¤");
for (service, stype, feature, pricing) in comparison {
println!(
" â {:<15} â {:<11} â {:<20} â {:<16} â",
service, stype, feature, pricing
);
}
println!(" âââââââââââââââââââ´ââââââââââââââ´âââââââââââââââââââââââ´âââââââââââââââââââ");
println!("\nđ S3-compatible storage examples completed!");
println!("\nâšī¸ S3-Compatible Storage Notes:");
println!(" âĸ All use the same S3Options structure");
println!(" âĸ Different endpoint URLs (handled by Tinify API)");
println!(" âĸ Region names vary between providers");
println!(" âĸ Some providers have different ACL systems");
println!(" âĸ Pricing and features differ significantly");
println!(" âĸ All provide cost savings compared to downloading/uploading manually");
cleanup_files(&["s3_compatible_input.png"]).await;
Ok(())
}
async fn create_test_image(filename: &str) -> Result<(), Box<dyn Error>> {
let png_data = create_simple_png_data();
tokio::fs::write(filename, png_data).await?;
println!("â
Created test image: {}", filename);
Ok(())
}
fn create_simple_png_data() -> Vec<u8> {
vec![
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, 0x00, 0x73, 0x7A, 0x7A, 0xF4, 0x00, 0x00, 0x00, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x62, 0x68, 0x60, 0x60, 0xF8, 0x0F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x30, 0x31, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82,
]
}
async fn cleanup_files(files: &[&str]) {
for file in files {
if tokio::fs::metadata(file).await.is_ok() {
let _ = tokio::fs::remove_file(file).await;
}
}
}