Created
September 4, 2023 04:10
-
-
Save TwistingTwists/87d2aecd5ef7f0f8af6f1a7ab2c9b15b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RazorNewWeb.RemoteUploadLive do | |
use RazorNewWeb, :live_view | |
@defaults %{ | |
# default params for upload in liveview | |
# 4000 MB | |
max_file_size: 4000 * 1000 * 1000, | |
chunk_size: 640 * 1000 * 3, | |
accept_upload_types: ~w( .mp4 ), | |
max_entries: 1 | |
} | |
@impl Phoenix.LiveView | |
def render(assigns) do | |
~H""" | |
<form id="video-upload-form" phx-submit="save" phx-change="validate"> | |
<.upload_component {assigns} /> | |
</form> | |
""" | |
end | |
def upload_component(assigns) do | |
~H""" | |
<div class="sm:grid sm:border-t sm:border-gray-200 sm:pt-5 "> | |
<%!-- render progress bar for upload --%> | |
<%= for entry <- @uploads.video.entries do %> | |
<div class=" text-center text-gray-600"> | |
<div | |
role="progressbar" | |
aria-valuemin="0" | |
aria-valuemax="100" | |
aria-valuenow={entry.progress} | |
style={"transition: width 0.5s ease-in-out; width: #{entry.progress}%; min-width: 1px;"} | |
class="col-span-full bg-purple-500 h-1.5 w-0 p-0" | |
> | |
</div> | |
<%= entry.progress %> % | |
<button | |
type="button" | |
phx-click="cancel-upload" | |
phx-value-ref={entry.ref} | |
aria-label="cancel" | |
> | |
× | |
</button> | |
</div> | |
<%= for err <- upload_errors(@uploads.video, entry) do %> | |
<p class="alert alert-danger"><%= error_to_string(err) %></p> | |
<% end %> | |
<% end %> | |
<%!-- upload drop zone --%> | |
<div class="mt-1 sm:mt-0" phx-drop-target={@uploads.video.ref}> | |
<div class="max-w-lg flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md hover:bg-gray-100"> | |
<div class="space-y-1 text-center"> | |
<svg | |
class="mx-auto h-12 w-12 text-gray-400" | |
stroke="currentColor" | |
fill="none" | |
viewBox="0 0 48 48" | |
aria-hidden="true" | |
> | |
<path | |
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" | |
stroke-width="2" | |
stroke-linecap="round" | |
stroke-linejoin="round" | |
> | |
</path> | |
</svg> | |
<div class="flex text-sm text-gray-600"> | |
<label | |
for={@uploads.video.ref} | |
class="relative cursor-pointer bg-white rounded-md font-medium text-yellow-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500" | |
> | |
<span phx-click={js_exec("##{@uploads.video.ref}", "click", [])}> | |
Upload files | |
</span> | |
<.live_file_input | |
upload={@uploads.video} | |
class=" hover:bg-blue-200 sr-only " | |
tabindex="0" | |
/> | |
</label> | |
<p class="pl-1">or drag and drop</p> | |
</div> | |
<p class="text-xs "> | |
<span class="text-red-400 font-bold"> | |
<%= Enum.join(assigns[:accepted_file_types] || [], ", ") %> | |
</span> | |
up to <%= trunc((assigns[:max_file_size] || 1) / 1_000_000) %> MB | |
</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
""" | |
end | |
@impl Phoenix.LiveView | |
def mount(params, _session, %{assigns: assigns} = socket) do | |
{ | |
:ok, | |
socket | |
|> allow_upload(:video, | |
auto_upload: true, | |
progress: &handle_progress/3, | |
accept: @defaults[:accept_upload_types], | |
max_entries: @defaults[:max_entries], | |
max_file_size: @defaults[:max_file_size], | |
chunk_size: @defaults[:chunk_size], | |
writer: &s3_writer/3 | |
) | |
|> assign(:accepted_file_types, @defaults[:accept_upload_types]) | |
} | |
end | |
def s3_writer(name, entry, socket) do | |
{RazorNewWeb.S3Writer, [s3_config: ExAws.Config.new(:s3), key: entry.client_name]} | |
end | |
def handle_progress(:video, entry, socket) do | |
{:noreply, socket} | |
end | |
@impl Phoenix.LiveView | |
def handle_event("cancel-upload", %{"ref" => ref}, socket) do | |
{:noreply, cancel_upload(socket, :video, ref)} | |
end | |
def handle_event("validate", params, socket) do | |
{:noreply, socket} | |
end | |
def error_to_string(:too_large), do: "Too large" | |
def error_to_string(:not_accepted), do: "You have selected an unacceptable file type" | |
def error_to_string(:too_many_files), do: "You have selected too many files" | |
defp max_uploaded(uploads), do: length(uploads.video.entries) < uploads.video.max_entries | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule RazorNewWeb.S3Writer do | |
@moduledoc """ | |
Module to stream video directly to S3 bucket. | |
But not via presigned url. | |
""" | |
@behaviour Phoenix.LiveView.UploadWriter | |
require Logger | |
@impl true | |
def init(opts) do | |
{s3_config, rest} = Keyword.pop(opts, :s3_config, ExAws.Config.new(:s3)) | |
bucket = s3_config.bucket | |
file_name = Keyword.fetch!(rest, :key) | |
with s3_op <- ExAws.S3.initiate_multipart_upload(bucket, file_name) do | |
{:ok, %{key: file_name, chunk: 1, s3_config: s3_config, s3_op: s3_op, parts: []}} | |
end | |
end | |
@impl true | |
def meta(state) do | |
state | |
end | |
@impl true | |
def write_chunk(data, state) do | |
part = | |
ExAws.S3.Upload.upload_chunk!({data, state.chunk}, state.s3_op, state.s3_config) | |
{:ok, %{state | chunk: state.chunk + 1, parts: [part | state.parts]}} | |
end | |
@impl true | |
def close(state, _reason) do | |
case ExAws.S3.Upload.complete(state.parts, state.s3_op, state.s3_config) do | |
{:ok, _} -> | |
{:ok, state} | |
{:error, reason} -> | |
{:error, reason} | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment