crypsol_storage

Crates.iocrypsol_storage
lib.rscrypsol_storage
version0.1.0
created_at2025-12-20 10:37:54.463688+00
updated_at2025-12-20 10:37:54.463688+00
descriptionAWS S3 storage library for Rust services with image processing, validation, and thumbnail generation.
homepage
repositoryhttps://github.com/crypsol/crypsol_storage
max_upload_size
id1996325
size114,625
Zuhair Thabit (zuhairthabit)

documentation

https://docs.rs/crypsol_storage

README

Crypsol Storage

A production-ready AWS S3 storage library for Rust services with image processing, validation, and thumbnail generation.

Crates.io Documentation License: MIT

Features

  • Image Upload with Processing: Automatic resizing and thumbnail generation
  • Multiple Format Support: JPEG, PNG, GIF, WebP
  • High-Quality Resizing: Uses Lanczos3 filter for optimal quality
  • Configurable Dimensions: Customize image and thumbnail sizes
  • File Validation: Content-type and file size validation
  • Presigned URLs: Generate temporary access URLs for private objects
  • Async/Await: Built on Tokio for high-performance async operations
  • Serde Support: Optional serialization support for API responses

Installation

Add to your Cargo.toml:

[dependencies]
crypsol_storage = "0.1"

With Serde support:

[dependencies]
crypsol_storage = { version = "0.1", features = ["serde"] }

Environment Variables

Variable Required Default Description
S3_AWS_ACCESS_KEY Yes - AWS Access Key ID
S3_AWS_SECRET_KEY Yes - AWS Secret Access Key
S3_AWS_REGION No us-east-1 AWS Region
S3_BUCKET_NAME No crypsol-storage S3 Bucket Name
S3_PUBLIC_BASE_URL No Auto-generated CDN or custom domain URL
S3_MAX_FILE_SIZE_MB No 5 Maximum file size in MB

Quick Start

Upload Image with Thumbnail

use crypsol_storage::{upload_image_with_config, ImageUploadConfig, Error};

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Read image bytes
    let image_data = std::fs::read("photo.jpg")?;
    
    // Upload with default config (200x200 main, 50x50 thumbnail)
    let config = ImageUploadConfig::default();
    let result = upload_image_with_config(&image_data, "image/jpeg", &config).await?;
    
    println!("Image URL: {}", result.url);
    println!("Thumbnail URL: {}", result.thumbnail_url);
    
    Ok(())
}

Upload with Custom Dimensions

use crypsol_storage::{upload_image_with_config, ImageUploadConfig};

let config = ImageUploadConfig {
    width: 800,
    height: 600,
    thumbnail_width: 150,
    thumbnail_height: 150,
    folder: "products".to_string(),
    maintain_aspect_ratio: true,
};

let result = upload_image_with_config(&image_data, "image/png", &config).await?;

Upload with Custom S3 Key

use crypsol_storage::upload_image_with_key;

// Full control over the S3 path
let result = upload_image_with_key(
    &image_data,
    "image/jpeg",
    "users/123/profile.jpg",  // Custom key
    200,   // width
    200,   // height
    50,    // thumbnail_width
    50,    // thumbnail_height
).await?;

Upload Raw File (No Processing)

use crypsol_storage::upload_file;

let pdf_data = std::fs::read("document.pdf")?;
let result = upload_file(&pdf_data, "application/pdf", "documents", "pdf").await?;

println!("File URL: {}", result.url);

Delete Image with Thumbnail

use crypsol_storage::delete_image_with_thumbnail;

// Deletes both main image and thumbnail
delete_image_with_thumbnail("profiles/2025/01/21/abc123.jpg").await?;

Check if File Exists

use crypsol_storage::file_exists;

if file_exists("profiles/2025/01/21/abc123.jpg").await? {
    println!("File exists!");
}

Generate Presigned URL

use crypsol_storage::generate_presigned_url;

// Generate URL valid for 1 hour (3600 seconds)
let url = generate_presigned_url("private/document.pdf", 3600).await?;
println!("Temporary URL: {}", url);

Extract Key from URL

use crypsol_storage::extract_key_from_url;

let url = "https://bucket.s3.us-east-1.amazonaws.com/profiles/image.jpg";
if let Some(key) = extract_key_from_url(url) {
    println!("Key: {}", key);  // "profiles/image.jpg"
}

Validation

Content Type Validation

use crypsol_storage::{validate_content_type, ALLOWED_IMAGE_TYPES};

// Check if content type is allowed
validate_content_type("image/jpeg")?;  // Ok
validate_content_type("application/pdf")?;  // Error

// Allowed types: image/jpeg, image/png, image/gif, image/webp
println!("Allowed types: {:?}", ALLOWED_IMAGE_TYPES);

File Size Validation

use crypsol_storage::{validate_file_size, get_max_file_size};

let file_size = 2 * 1024 * 1024;  // 2MB
validate_file_size(file_size)?;  // Ok if under limit

println!("Max size: {} bytes", get_max_file_size());

Error Handling

use crypsol_storage::Error;

match upload_image_with_config(&data, content_type, &config).await {
    Ok(result) => println!("Uploaded: {}", result.url),
    Err(Error::InvalidFileType(got, allowed)) => {
        println!("Invalid type: {}. Allowed: {}", got, allowed);
    }
    Err(Error::FileTooLarge(size, max)) => {
        println!("File too large: {} bytes (max: {})", size, max);
    }
    Err(Error::ImageProcessing(msg)) => {
        println!("Image processing failed: {}", msg);
    }
    Err(Error::AwsS3(msg)) => {
        println!("S3 error: {}", msg);
    }
    Err(e) => println!("Error: {}", e),
}

S3 Bucket Configuration

Required IAM Permissions

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:HeadObject"
            ],
            "Resource": "arn:aws:s3:::your-bucket-name/*"
        }
    ]
}

CORS Configuration (for browser uploads)

[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
        "AllowedOrigins": ["https://your-domain.com"],
        "ExposeHeaders": ["ETag"],
        "MaxAgeSeconds": 3600
    }
]

License

MIT License - see LICENSE for details.

Author

Zuhair Thabit - admin@crypsol.tech

Commit count: 0

cargo fmt