Storage - Usage guide¶
This guide shows you how to use the Storage service in your FastEdgy application.
Configuration¶
Set the storage path in your environment file (.env
):
File organization¶
Files are organized based on the directory_path
you provide in your upload calls:
- Workspace storage (
global_storage=False
):{DATA_PATH}/{workspace_id}/{directory_path}/
- Global storage (
global_storage=True
):{DATA_PATH}/{directory_path}/
Example with DATA_PATH=./storage
:
storage/
├── 123/ # workspace_id=123 files
│ ├── photos/ # directory_path="photos"
│ │ └── image.jpg
│ └── avatars/ # directory_path="avatars"
│ └── avatar.png
├── 456/ # workspace_id=456 files
│ └── documents/ # directory_path="documents"
│ └── file.pdf
├── media/ # global_storage=True, directory_path="media"
│ └── shared.jpg
└── public/ # global_storage=True, directory_path="public"
└── logo.png
File upload¶
Basic upload¶
from fastedgy.app import FastEdgy
from fastedgy.dependencies import Inject
from fastedgy.storage import Storage
from fastapi import UploadFile, File
app = FastEdgy()
@app.post("/upload")
async def upload_file(
file: UploadFile = File(...),
directory: str = "photos",
storage: Storage = Inject(Storage)
):
# Upload to workspace-specific directory
file_path = await storage.upload(
file=file,
directory_path=directory
)
return {"path": file_path, "filename": file.filename}
Upload with custom filename¶
@app.post("/upload-avatar")
async def upload_avatar(
file: UploadFile = File(...),
storage: Storage = Inject(Storage)
):
file_path = await storage.upload(
file=file,
directory_path="avatars",
filename="avatar.{ext}" # {ext} is replaced with file extension
)
return {"avatar_path": file_path}
Global storage¶
@app.post("/upload-global")
async def upload_global(
file: UploadFile = File(...),
directory: str = "shared",
storage: Storage = Inject(Storage)
):
# Upload to global directory (shared across workspaces)
file_path = await storage.upload(
file=file,
directory_path=directory,
global_storage=True
)
return {"path": file_path}
Model field upload¶
Use the built-in API endpoints to upload directly to model fields:
from fastedgy.orm import Model, fields
from fastedgy.api_route_model import api_route_model
@api_route_model()
class User(Model):
name = fields.CharField(max_length=100)
avatar = fields.CharField(max_length=255, null=True)
class Meta:
tablename = "users"
Upload to the model field:
# Upload avatar for user ID 123
POST /storage/upload/user/123/avatar
Content-Type: multipart/form-data
file: [image file]
Download files¶
The built-in endpoint handles file serving:
# Download file
GET /storage/download/photos/image.jpg
# Force download (with Content-Disposition header)
GET /storage/download/photos/image.jpg?force_download=true
Image optimization¶
FastEdgy automatically optimizes images when you add URL parameters. Optimized images are cached for better performance.
Resize images¶
# Resize by width (maintains aspect ratio)
GET /storage/download/photos/image.jpg?w=300
# Resize by height (maintains aspect ratio)
GET /storage/download/photos/image.jpg?h=200
# Resize to fit within 800x600 box
GET /storage/download/photos/image.jpg?w=800&h=600
Resize modes¶
Control how images are resized when both width and height are specified:
# Contain: fit inside box (no cropping, may have empty space)
GET /storage/download/photos/image.jpg?w=300&h=200&m=contain
# Cover: fill entire box (crops if needed for perfect fit)
GET /storage/download/photos/image.jpg?w=300&h=200&m=cover
Format conversion¶
Convert images to modern formats for better performance:
# Convert to WebP (smaller file size)
GET /storage/download/photos/image.jpg?e=webp
# Convert to PNG
GET /storage/download/photos/image.jpg?e=png
# Resize and convert in one request
GET /storage/download/photos/image.jpg?w=500&e=webp
Common use cases¶
User avatars (square thumbnails):
Responsive images (different sizes for mobile/desktop):
# Mobile
GET /storage/download/photos/hero.jpg?w=400&e=webp
# Desktop
GET /storage/download/photos/hero.jpg?w=1200&e=webp
Product thumbnails (consistent grid layout):
URL parameters¶
w
: Width in pixelsh
: Height in pixelsm
: Resize mode (contain
orcover
)e
: Output format (jpg
,png
,webp
)force_download
: Force file download instead of display
Upload from URL¶
@app.post("/upload-from-url")
async def upload_from_url(
url: str,
directory: str = "external",
storage: Storage = Inject(Storage)
):
file_path = await storage.download_and_upload(
file_url=url,
directory_path=directory
)
return {"path": file_path}
Upload from base64¶
@app.post("/upload-base64")
async def upload_base64(
data: str, # base64-encoded image
directory: str = "images",
storage: Storage = Inject(Storage)
):
file_path = await storage.upload_from_base64(
data=data,
directory_path=directory
)
return {"path": file_path}
Error handling¶
The Storage service validates files automatically:
- File type: Only images are accepted
- Extensions: JPG, JPEG, PNG, GIF, WEBP
- Filename: Must be provided
@app.post("/safe-upload")
async def safe_upload(
file: UploadFile = File(...),
directory: str = "files",
storage: Storage = Inject(Storage)
):
try:
file_path = await storage.upload(
file=file,
directory_path=directory
)
return {"success": True, "path": file_path}
except ValueError as e:
return {"success": False, "error": str(e)}