S3 storage adapter for Plato image fields using ExAws.
Supports AWS S3 and S3-compatible services like SeaweedFS, MinIO, and others.
Dependencies
Add these to your mix.exs to use image fields:
def deps do
[
# x-release-please-start-version
{:plato, "~> 0.0.19"},
# x-release-please-end
# Required for image fields
{:ex_aws, "~> 2.5"},
{:ex_aws_s3, "~> 2.5"},
{:hackney, "~> 1.20"}
]
endConfiguration
Configure storage in config/config.exs or config/runtime.exs:
# AWS S3 (production)
config :my_app, :plato,
repo: MyApp.Repo,
storage: [
adapter: Plato.Storage.S3Adapter,
bucket: "my-app-uploads",
region: "us-east-1",
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
signed_url_expiry: 3600 # URL expiry in seconds
]
# SeaweedFS (local development)
config :my_app, :plato,
repo: MyApp.Repo,
storage: [
adapter: Plato.Storage.S3Adapter,
bucket: "plato-uploads",
endpoint: "http://localhost:8333",
access_key_id: "any-key",
secret_access_key: "any-secret",
region: "us-east-1"
]Configuration Options
Required:
:adapter- Must bePlato.Storage.S3Adapter:bucket- S3 bucket name:access_key_id- AWS/S3 access key:secret_access_key- AWS/S3 secret key
Optional:
:region- AWS region (default: "us-east-1"):endpoint- Custom endpoint for S3-compatible services:internal_endpoint- Endpoint for server-side operations (useful in Docker):signed_url_expiry- URL expiration in seconds (default: 3600)
Local Development with SeaweedFS
SeaweedFS provides an S3-compatible API perfect for local development.
Add to docker-compose.yml:
services:
seaweedfs:
image: chrislusf/seaweedfs:latest
command: "server -s3 -dir=/data"
ports:
- "8333:8333" # S3 API
- "9333:9333" # Master
- "8080:8080" # Volume
volumes:
- seaweedfs_data:/data
volumes:
seaweedfs_data:Create bucket:
curl -X POST 'http://localhost:8080/buckets' \
-H 'Content-Type: application/json' \
-d '{"name":"plato-uploads"}'See the Plato Starter Repository for a complete working example with SeaweedFS.
Upload Size Limits
Configure Phoenix to accept larger uploads in your endpoint:
# lib/my_app_web/endpoint.ex
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library(),
length: 100_000_000 # 100MB limit (default is 8MB)Image Field Usage
Once configured, image fields can be added to schemas:
schema "blog-post" do
field :title, :text
field :cover_image, :image
field :body, :text, multiline: true
endUploaded images are stored with metadata:
%{
"url" => "http://localhost:8333/bucket/path/to/image.jpg",
"storage_path" => "app/schema/field/timestamp_hash_filename.jpg",
"filename" => "original-filename.jpg",
"content_type" => "image/jpeg",
"size_bytes" => 245678
}Access in templates:
<%= if post.cover_image do %>
<img src="<%= post.cover_image["url"] %>" alt="<%= post.title %>">
<% end %>