# `Storehouse.Live`
[🔗](https://github.com/hattoriai/storehouse/blob/v0.1.0/lib/storehouse/live.ex#L2)

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](liveview.md) guide.

# `result`

```elixir
@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.

# `allow`

```elixir
@spec allow(Phoenix.LiveView.Socket.t(), atom() | String.t(), keyword()) ::
  Phoenix.LiveView.Socket.t()
```

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`

```elixir
@spec consume(Phoenix.LiveView.Socket.t(), atom() | String.t(), keyword()) ::
  {Phoenix.LiveView.Socket.t(), [result()]}
```

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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
