# Quick Start

This page shows the two most common setups — local filesystem and AWS S3 — so you can be productive in minutes.

## Local Storage

Good for development and single-server deployments.

    from granite_storage import StorageManager, StoragePolicy
    from granite_storage.backends.local import LocalStorageBackend

    # 1. Create a backend pointing at a directory on disk.
    backend = LocalStorageBackend(root_dir="/var/app/uploads")

    # 2. Define policies (one per "storage slot").
    policies = {
        "avatars": StoragePolicy(
            storage_key="avatars",
            backend_key="local",
            max_size=2 * 1024 * 1024,  # 2 MB
            key_prefix="avatars",
        ),
        "documents": StoragePolicy(
            storage_key="documents",
            backend_key="local",
            max_size=10 * 1024 * 1024,  # 10 MB
            key_prefix="docs",
        ),
    }

    # 3. Build the manager.
    manager = StorageManager(backends={"local": backend}, policies=policies)

    # 4. Store a file from bytes.
    ref = manager.put_bytes(
        storage_key="avatars",
        model_name="user",
        entity_id="user-123",
        field_name="avatar",
        content=open("photo.jpg", "rb").read(),
        content_type="image/jpeg",
        original_filename="photo.jpg",
    )
    print(ref.location)   # avatars/user/user-123/avatar/photo.jpg

    # 5. Stream a large file (memory-safe).
    with open("report.pdf", "rb") as f:
        ref = manager.put_stream(
            storage_key="documents",
            model_name="report",
            entity_id="rpt-456",
            field_name="file",
            stream=f,
            content_type="application/pdf",
            original_filename="report.pdf",
        )

    # 6. Retrieve content.
    data: bytes = manager.get(ref)

    # 7. Open as a file-like object (streaming read).
    with manager.open(ref) as fh:
        first_bytes = fh.read(512)

    # 8. Delete.
    manager.delete(ref)

## S3 Storage

Replace the backend with `S3StorageBackend`. Everything else stays the same.

    import boto3
    from granite_storage import StorageManager, StoragePolicy
    from granite_storage.backends.s3 import S3StorageBackend

    s3_client = boto3.client("s3", region_name="us-east-1")

    backend = S3StorageBackend(
        bucket="my-app-uploads",
        prefix="production",          # optional key prefix inside the bucket
        client=s3_client,
        extra_put_kwargs={"ACL": "private"},  # any extra boto3 put_object kwargs
    )

    manager = StorageManager(
        backends={"s3": backend},
        policies={
            "avatars": StoragePolicy(
                storage_key="avatars",
                backend_key="s3",
                max_size=2 * 1024 * 1024,
                key_prefix="avatars",
            ),
        },
    )

    # API is identical to local storage.
    with open("photo.jpg", "rb") as f:
        ref = manager.put_stream(
            storage_key="avatars",
            model_name="user",
            entity_id="user-123",
            field_name="avatar",
            stream=f,
            content_type="image/jpeg",
            original_filename="photo.jpg",
        )

## Mixed Backends

You can register multiple backends in a single `StorageManager` and route each policy to a different one:

    from granite_storage.backends.local import LocalStorageBackend
    from granite_storage.backends.s3 import S3StorageBackend

    manager = StorageManager(
        backends={
            "local": LocalStorageBackend(root_dir="/tmp/dev"),
            "s3":    S3StorageBackend(bucket="prod-bucket"),
        },
        policies={
            "thumbnails": StoragePolicy("thumbnails", backend_key="local", max_size=512*1024),
            "originals":  StoragePolicy("originals",  backend_key="s3",    max_size=50*1024*1024),
        },
    )

## Error Handling

    from granite_storage import ContentTooLargeError, StorageError

    try:
        ref = manager.put_bytes(storage_key="avatars", ..., content=huge_bytes)
    except ContentTooLargeError as e:
        # HTTP 413 — content exceeds the policy max_size
        print(f"File too large: {e}")
    except StorageError as e:
        # Any other storage problem (missing policy, backend error, etc.)
        print(f"Storage error: {e}")