Current section

Files

Jump to
pg_large_objects lib pg_large_objects upload_writer.ex
Raw

lib/pg_large_objects/upload_writer.ex

if Code.ensure_loaded?(Phoenix.LiveView.UploadWriter) do
defmodule PgLargeObjects.UploadWriter do
@moduledoc """
LiveView UploadWriter streaming data to Postgres large object.
This module can be used with `Phoenix.LiveView.allow_upload/3` to specify
that file uploads by clients should be streamed straight to large objects in
the database. Pass the `:repo` option in the second element of the tuple
returned by the function passed to `:writer` to indicate which database the
object should be created in, e.g.:
```elixir
socket
|> allow_upload(:photo,
accept: :any,
writer: fn _name, _entry, _socket ->
{PgLargeObjects.UploadWriter, repo: MyApp.Repo}
end
)
```
The object ID of the uploaded file is available in the meta data available
to the callback given to `Phoenix.LiveView.consume_uploaded_entries/3`:
```elixir
consume_uploaded_entries(socket, :photo, fn meta, _entry ->
%{object_id: object_id} = meta
# Store `object_id` in database to retain handle to uploaded data.
{:ok, nil}
end)
```
See `Phoenix.LiveView.UploadWriter` for further information.
"""
@behaviour Phoenix.LiveView.UploadWriter
alias PgLargeObjects.LargeObject
@impl Phoenix.LiveView.UploadWriter
def init(opts) do
repo = Keyword.fetch!(opts, :repo)
with {:ok, object} <- LargeObject.create(repo) do
{:ok, %{object_id: object.oid, repo: repo}}
end
end
@impl Phoenix.LiveView.UploadWriter
def meta(state), do: state
@impl Phoenix.LiveView.UploadWriter
def write_chunk(data, state) do
%{object_id: object_id, repo: repo} = state
result =
repo.transaction(fn ->
result =
with {:ok, object} <- LargeObject.open(repo, object_id, mode: :append),
:ok <- LargeObject.write(object, data) do
LargeObject.close(object)
end
case result do
:ok -> state
{:error, error} -> repo.rollback(error)
end
end)
case result do
{:ok, state} -> {:ok, state}
{:error, error} -> {:error, error, state}
end
end
@impl Phoenix.LiveView.UploadWriter
def close(state, _reason) do
{:ok, state}
end
end
end