Storehouse.Live (Storehouse v0.1.0)

Copy Markdown View Source

The files of a LiveView upload, stored.

def mount(_params, _session, socket) do
  {:ok,
   socket
   |> assign(:photos, [])
   |> allow_upload(:photos,
     accept: ~w(.png .jpg .jpeg .gif .webp),
     max_file_size: 10_000_000,
     auto_upload: true,
     progress: &handle_progress/3
   )}
end

defp handle_progress(:photos, _entry, socket) do
  {socket, results} = Storehouse.Live.consume(socket, :photos, accept: Storehouse.Check.images())

  socket =
    Enum.reduce(results, socket, fn
      {_entry, {:ok, file}}, socket -> update(socket, :photos, &[file | &1])
      {entry, {:error, reason}}, socket -> put_flash(socket, :error, "#{entry.client_name}: #{inspect(reason)}")
    end)

  {:noreply, socket}
end

The size limits and the accepted types are the upload's (its accept and max_file_size); each file is checked again on the server, by its bytes (Storehouse.Check), and :accept refuses the types the app does not take. See the LiveView guide.

Summary

Types

What became of an entry: its stored file, or why it was not stored.

Functions

Allows an upload (Phoenix.LiveView.allow_upload/3) whose files go to the store in one of three ways (:mode)

Stores the finished entries of upload_name, and cancels those the upload refused. Gives the socket and a result for each of them, in the entries' order.

Types

result()

@type result() ::
  {Phoenix.LiveView.UploadEntry.t(),
   {:ok, Storehouse.File.t()} | {:error, term()}}

What became of an entry: its stored file, or why it was not stored.

Functions

allow(socket, upload_name, opts)

Allows an upload (Phoenix.LiveView.allow_upload/3) whose files go to the store in one of three ways (:mode):

  • :direct - straight from the browser to the store, at a URL the app signed: a large file never goes through the app. The default, for a store that signs uploads (Storehouse.S3, and Storehouse.Local in development). The browser needs Storehouse's uploader (below).
  • :stream - through the app, on to the store as each chunk comes (Storehouse.Live.Writer): with no file on the app's disk, checked as it comes, and with no CORS, as the browser sends it to the app.
  • :server - a plain upload: to a temporary file on the app's disk, then to the store (consume/3). The default for a store that takes neither.

Either way, consume/3 gives the stored, checked files.

socket
|> Storehouse.Live.allow(:photos,
  accept: ~w(.png .jpg .jpeg .gif .webp),
  max_file_size: 50_000_000,
  auto_upload: true,
  progress: &handle_progress/3
)

With :direct, the browser needs Storehouse's uploader, in app.js:

import {Uploaders} from "storehouse"
let liveSocket = new LiveSocket("/live", Socket, {uploaders: Uploaders, ...})

Options

The options of Phoenix.LiveView.allow_upload/3 (but :external and :writer), and:

  • :mode - :direct, :stream or :server, as above.
  • :adapter - the store. The default is Storehouse.adapter/0; give the same to consume/3.
  • :cache - true stores the files under the temporary prefix, until the record that has them is saved (Storehouse.Ecto).
  • :key - a function of the name and the claimed type that gives the key, as for Storehouse.store/2.
  • :types, :max_pixels - with :stream, the checked types taken and the pixels at most, checked as the file comes. (With the other modes, give consume/3 its :accept and :max_pixels.)
  • :expires_in - with :direct, how long the browser has to start each upload, in seconds. The default is 600.

consume(socket, upload_name, opts \\ [])

Stores the finished entries of upload_name, and cancels those the upload refused. Gives the socket and a result for each of them, in the entries' order.

  • An entry that is done is consumed and stored with Storehouse.store/2 (its name and claimed type are the client's), or, sent straight to the store (allow/3), checked there with Storehouse.verify_upload/2: {:ok, file}, or {:error, reason} when the check or the store refuses it (and a file refused there is deleted).
  • An entry with an error (too large, a type that is not accepted, too many files) is cancelled: {:error, reason}, the upload's (:too_large, :not_accepted, :too_many_files, …).
  • An entry still in progress is left alone, and has no result.

An entry is consumed once: a call before LiveView has dropped an entry that an earlier call consumed leaves it alone. So the function can run in the progress callback and in a form's change handler, with files that finish at different times.

The options are those of Storehouse.store/2, but :name and :content_type.