Signs uploads straight to the store for a frontend that is not a
LiveView: a JSON endpoint in front of Storehouse.Batch.presign/2.
# lib/my_app_web/router.ex, behind your authentication
scope "/files" do
pipe_through [:files, :require_user] # the guide's, with its CSRF check
forward "/presign", Storehouse.Plug.Presign,
owner: &MyAppWeb.Files.owner/1,
room: &MyAppWeb.Files.room/1,
existing: &MyAppWeb.Files.existing/2,
accept: Storehouse.Check.images(),
max_bytes: 50_000_000
forward "/register", Storehouse.Plug.Register, ...
endA POST of
{"uploads": [{"name": "harbour.jpg", "type": "image/jpeg", "bytes": 35367, "checksum": "e3b0…"}]}is answered 200 with one result per upload, in order:
{"uploads": [
{"upload": {"key": "accounts/42/2026/10/…-harbour.jpg", "method": "PUT", "url": "https://…", "headers": {"content-type": "image/jpeg", …}}},
{"existing": {"key": …, "url": …, …}},
{"error": "storage_full"}
]}The browser sends each file to its url with its method and exactly
its headers, then gives the keys to Storehouse.Plug.Register. A body
that is not that JSON gives 400, one that is not application/json
415 (a form of another site is never read), and a batch larger than :max_items
413, with {"error": code}.
Options
:owner- a function of the conn that gives the owner whose files these are (Storehouse.store/2's:owner), ornil.:room- a function of the conn that gives the bytes the owner may still store, shared by the batch, or:infinity.:existing- a function of the conn and a checksum that gives the file the app has already with it, ornil(:existingofStorehouse.presign_upload/4).- the other options of
Storehouse.Batch.presign/2(:accept,:max_bytes,:key,:cache,:expires_in,:adapter,:max_items), as they are.