FileStorage

Trait FileStorage 

Source
pub trait FileStorage: Send + Sync {
    // Required methods
    fn store<'life0, 'async_trait>(
        &'life0 self,
        file: UploadedFile,
    ) -> Pin<Box<dyn Future<Output = StorageResult<StoredFile>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
    fn retrieve<'life0, 'life1, 'async_trait>(
        &'life0 self,
        id: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<Vec<u8>>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn delete<'life0, 'life1, 'async_trait>(
        &'life0 self,
        id: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn url<'life0, 'life1, 'async_trait>(
        &'life0 self,
        id: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<String>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn exists<'life0, 'life1, 'async_trait>(
        &'life0 self,
        id: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
    fn get_metadata<'life0, 'life1, 'async_trait>(
        &'life0 self,
        id: &'life1 str,
    ) -> Pin<Box<dyn Future<Output = StorageResult<StoredFile>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait;
}
Expand description

Abstraction for file storage backends

This trait provides a unified interface for storing, retrieving, and managing files across different storage backends (local filesystem, S3, Azure Blob, etc.).

§Design Principles

  • Backend Agnostic: Handlers don’t need to know about storage implementation
  • Async First: All operations are async for optimal I/O performance
  • Type Safe: Strong types prevent common errors
  • Production Ready: Built-in support for streaming, validation, and error handling

§Implementation Requirements

Implementations must:

  • Generate unique identifiers for stored files (UUIDs recommended)
  • Handle concurrent access safely
  • Provide atomic operations where possible
  • Clean up resources on errors

§Examples

use acton_htmx::storage::{FileStorage, LocalFileStorage, UploadedFile};
use std::path::PathBuf;

// Create storage backend
let storage = LocalFileStorage::new(PathBuf::from("/var/uploads"))?;

// Store a file
let file = UploadedFile::new("avatar.png", "image/png", vec![/* ... */]);
let stored = storage.store(file).await?;

// Retrieve the file
let data = storage.retrieve(&stored.id).await?;

// Get file URL (for serving to clients)
let url = storage.url(&stored.id).await?;

// Delete when no longer needed
storage.delete(&stored.id).await?;

Required Methods§

Source

fn store<'life0, 'async_trait>( &'life0 self, file: UploadedFile, ) -> Pin<Box<dyn Future<Output = StorageResult<StoredFile>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Stores an uploaded file and returns metadata about the stored file

This method should:

  • Generate a unique ID for the file
  • Persist the file data to the storage backend
  • Return metadata that can be used to retrieve the file later
§Errors

Returns an error if:

  • The storage backend is unavailable
  • There’s insufficient storage space
  • File I/O fails
§Examples
let file = UploadedFile::new("report.pdf", "application/pdf", vec![/* ... */]);
let stored = storage.store(file).await?;
println!("Stored with ID: {}", stored.id);
Source

fn retrieve<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = StorageResult<Vec<u8>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Retrieves file data by ID

§Errors

Returns an error if:

  • The file doesn’t exist (StorageError::NotFound)
  • The storage backend is unavailable
  • File I/O fails
§Examples
let data = storage.retrieve("550e8400-e29b-41d4-a716-446655440000").await?;
println!("Retrieved {} bytes", data.len());
Source

fn delete<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = StorageResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Deletes a file by ID

This operation should be idempotent - deleting a non-existent file should not return an error.

§Errors

Returns an error if:

  • The storage backend is unavailable
  • File I/O fails (permissions, etc.)
§Examples
storage.delete("550e8400-e29b-41d4-a716-446655440000").await?;
println!("File deleted successfully");
Source

fn url<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = StorageResult<String>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Returns a URL for accessing the file

The returned URL format depends on the storage backend:

  • Local storage: relative path (e.g., “/uploads/abc123/file.jpg”)
  • S3: presigned URL or public URL
  • CDN: CDN URL
§Errors

Returns an error if:

  • The file doesn’t exist
  • URL generation fails (e.g., S3 presigning error)
§Examples
let url = storage.url("550e8400-e29b-41d4-a716-446655440000").await?;
println!("File available at: {}", url);
Source

fn exists<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = StorageResult<bool>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Checks if a file exists

This is useful for validating file references before attempting retrieval.

§Examples
if storage.exists("550e8400-e29b-41d4-a716-446655440000").await? {
    println!("File exists!");
}
Source

fn get_metadata<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = StorageResult<StoredFile>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Retrieves file metadata by ID

This method retrieves only the metadata (filename, content type, size, etc.) without reading the actual file data. This is useful for serving files with proper Content-Type headers and other metadata.

§Errors

Returns an error if:

  • The file doesn’t exist (StorageError::NotFound)
  • The storage backend is unavailable
  • Metadata cannot be read
§Examples
let metadata = storage.get_metadata("550e8400-e29b-41d4-a716-446655440000").await?;
println!("Content-Type: {}", metadata.content_type);
println!("Size: {} bytes", metadata.size);

Implementors§