Current section

Files

Jump to
buckets lib buckets.ex
Raw

lib/buckets.ex

defmodule Buckets do
@version Mix.Project.config()[:version]
@moduledoc """
Cloud-agnostic file storage for Elixir with Phoenix integration.
Buckets provides a unified API for working with file storage across different
cloud providers. Whether you're using local filesystem storage for development,
Google Cloud Storage, Amazon S3, or other S3-compatible providers, Buckets
offers a consistent interface with powerful features.
## Features
- **Multiple Storage Adapters** - Support for filesystem, Google Cloud Storage,
Amazon S3, and S3-compatible providers (Cloudflare R2, DigitalOcean Spaces, Tigris)
- **Direct Uploads** - Upload files directly from browsers to cloud storage,
bypassing your Phoenix server
- **Signed URLs** - Generate time-limited, secure URLs for private files
- **LiveView Integration** - Seamless integration with Phoenix LiveView's
file upload functionality
- **Dynamic Configuration** - Switch storage providers at runtime for
multi-tenant applications
- **Development Tools** - Built-in router for local file uploads/downloads
during development
- **Telemetry** - Comprehensive instrumentation for monitoring and debugging
## Quick Start
1. Add `buckets` to your dependencies:
```elixir
def deps do
[{:buckets, "~> #{@version}"}]
end
```
2. Create a Cloud module:
```elixir
defmodule MyApp.Cloud do
use Buckets.Cloud, otp_app: :my_app
end
```
3. Configure your adapter:
```elixir
# config/dev.exs
config :my_app, MyApp.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "priv/uploads",
base_url: "http://localhost:4000"
```
4. Upload files:
```elixir
# From a Plug.Upload or Phoenix.LiveView.UploadEntry
{:ok, object} = MyApp.Cloud.insert(upload)
# From a file path
object = Buckets.Object.from_file("photo.jpg")
{:ok, stored} = MyApp.Cloud.insert(object)
# Generate a signed URL
{:ok, url} = MyApp.Cloud.url(stored, expires: 3600)
```
## Storage Adapters
Buckets includes these built-in adapters:
- `Buckets.Adapters.Volume` - Local filesystem storage
- `Buckets.Adapters.S3` - Amazon S3 and S3-compatible services
- `Buckets.Adapters.GCS` - Google Cloud Storage
See the adapter modules for specific configuration options.
## Next Steps
- See `Buckets.Cloud` for the high-level API
- See `Buckets.Object` for working with file objects
- Read the [Getting Started](guides/introduction/getting-started.html) guide
- Learn about [Direct Uploads with LiveView](guides/howtos/direct-uploads-liveview.html)
"""
alias Buckets.Telemetry
@doc """
Delegates a `put/3` function call to the configured `:adapter`.
"""
def put(%Buckets.Object{} = object, remote_path, config) do
{adapter, config} = Keyword.pop!(config, :adapter)
metadata = %{
adapter: adapter,
filename: object.filename,
path: remote_path,
content_type: object.metadata[:content_type]
}
Telemetry.span([:buckets, :adapter, :put], metadata, fn ->
adapter.put(object, remote_path, config)
end)
end
@doc """
Delegates a `get/2` function call to the configured `:adapter`.
"""
def get(remote_path, config) do
{adapter, config} = Keyword.pop!(config, :adapter)
metadata = %{
adapter: adapter,
path: remote_path
}
Telemetry.span([:buckets, :adapter, :get], metadata, fn ->
adapter.get(remote_path, config)
end)
end
@doc """
Delegates a `url/2` function call to the configured `:adapter`.
"""
def url(remote_path, config) do
{adapter, config} = Keyword.pop!(config, :adapter)
metadata = %{
adapter: adapter,
path: remote_path
}
Telemetry.span([:buckets, :adapter, :url], metadata, fn ->
adapter.url(remote_path, config)
end)
end
@doc """
Delegates a `copy/3` function call to the configured `:adapter`.
"""
def copy(source_path, destination_path, config) do
{adapter, config} = Keyword.pop!(config, :adapter)
metadata = %{
adapter: adapter,
source_path: source_path,
destination_path: destination_path
}
Telemetry.span([:buckets, :adapter, :copy], metadata, fn ->
adapter.copy(source_path, destination_path, config)
end)
end
@doc """
Delegates a `delete/2` function call to the configured `:adapter`.
"""
def delete(remote_path, config) do
{adapter, config} = Keyword.pop!(config, :adapter)
metadata = %{
adapter: adapter,
path: remote_path
}
Telemetry.span([:buckets, :adapter, :delete], metadata, fn ->
adapter.delete(remote_path, config)
end)
end
end