Storehouse.S3 (Storehouse v0.1.0)

Copy Markdown View Source

A Storehouse.Adapter for Amazon S3, and the stores that speak its API: Cloudflare R2, MinIO, Tigris, Backblaze B2, DigitalOcean Spaces, Google Cloud Storage's interoperability. With no dependency: it signs its requests itself (AWS Signature Version 4).

# config/runtime.exs
config :storehouse, adapter: Storehouse.S3

config :storehouse, Storehouse.S3,
  bucket: "my-app-uploads",
  region: "eu-west-1",
  access_key_id: System.fetch_env!("AWS_ACCESS_KEY_ID"),
  secret_access_key: System.fetch_env!("AWS_SECRET_ACCESS_KEY")

For another store, its endpoint (whose URLs name the bucket in the path, path_style, unless told otherwise):

config :storehouse, Storehouse.S3,
  bucket: "uploads",
  region: "auto",
  endpoint: "https://<account>.r2.cloudflarestorage.com",
  access_key_id: System.fetch_env!("R2_ACCESS_KEY_ID"),
  secret_access_key: System.fetch_env!("R2_SECRET_ACCESS_KEY"),
  public_url: "https://files.example.com"

Options

  • :bucket, :region, :access_key_id, :secret_access_key (required), and :session_token for temporary credentials.
  • :endpoint - the store's URL. The default is AWS's, https://s3.<region>.amazonaws.com.
  • :path_style - whether URLs name the bucket in their path (https://endpoint/bucket/key) rather than their host (https://bucket.endpoint/key). The default is false for AWS, true with an :endpoint.
  • :public_url - the base URL of the files for url/1: a CDN, a custom domain. The default is the bucket's URL. A bucket that is not public serves its files at signed URLs (Storehouse.signed_url/2).

Each file is stored with its checked content type, and with Content-Disposition: attachment for every type a browser should not show (Storehouse.Check.inline?/1): the store never serves an uploaded page as a page.

Uploads straight to the bucket

presign_upload/3 signs a PUT of one file, of its type and its size exactly, which the browser sends to the bucket without going through the app (Storehouse.Live.allow/3). The bucket must allow it with CORS:

[{"AllowedOrigins": ["https://my-app.example.com"],
  "AllowedMethods": ["PUT"],
  "AllowedHeaders": ["content-type", "content-disposition"],
  "MaxAgeSeconds": 3600}]