Uploads

에단(손형규)·2022년 7월 2일

LiveView는 서버에 직접 업로드는 물론 클라이언트에서 클라우드에 직접 외부 업로드에 대한 진행 상황과 함께 대화형 파일 업로드를 지원합니다.

Built-in Features

  • Accept specification - 허용되는 파일 유형, 최대 항목 수, 최대 파일 크기 등을 정의합니다. 클라이언트가 파일을 선택하면 파일 메타데이터가 사양에 대해 자동으로 검증됩니다. Phoenix.LiveView.allow_upload/3을 참조하십시오.

  • Reactive entries - 업로드는 소켓의 @uploads 할당에 채워집니다. 항목은 진행 상황, 오류, 취소 등에 자동으로 응답합니다.

  • Drag and drop - phx-drop-target 속성을 사용하여 활성화합니다. Phoenix.LiveView.Helpers.live_file_input/2를 참조하십시오.

Allow uploads

일반적으로 마운트 시 allow_upload/3을 통해 업로드를 활성화합니다.

@impl Phoenix.LiveView
def mount(_params, _session, socket) do
  {:ok,
   socket
   |> assign(:uploaded_files, [])
   |> allow_upload(:avatar, accept: ~w(.jpg .jpeg), max_entries: 2)}
end

지금은 그게 다야! 나중에 일부 양식 및 업로드 관련 콜백을 구현하기 위해 LiveView로 돌아올 것이지만 업로드와 관련된 대부분의 기능은 템플릿에서 발생합니다.

Render reactive elements

Phoenix.LiveView.Helpers.live_file_input/2 파일 입력 생성기를 사용하여 업로드용 파일 입력을 렌더링합니다.

<%# lib/my_app_web/live/upload_live.html.heex %>

<form id="upload-form" phx-submit="save" phx-change="validate">
  <%= live_file_input @uploads.avatar %>
  <button type="submit">Upload</button>
</form>

중요: 양식에서 phx-submit 및 phx-change를 바인딩해야 합니다.

live_file_input/2를 사용하면 파일 입력에 추가 속성을 설정할 수 있지만 id, accept 및 multiple과 같은 많은 속성은 allow_upload/3 사양에 따라 자동으로 설정됩니다.

템플릿에 대한 사후 업데이트는 최종 사용자가 파일 입력과 상호 작용할 때 발생합니다.

Upload entries

업로드는 소켓의 @uploads 할당에 채워집니다. 허용된 각 업로드에는 allow_upload/3 사양의 :max_entries 값에 관계없이 항목 목록이 포함됩니다. 이러한 항목 구조에는 진행률, 클라이언트 파일 정보, 오류 등을 포함하여 업로드에 대한 모든 정보가 포함됩니다.

주석이 달린 예를 살펴보겠습니다.

<%# lib/my_app_web/live/upload_live.html.heex %>

<%# use phx-drop-target with the upload ref to enable file drag and drop %>
<section phx-drop-target={@uploads.avatar.ref}>

<%# render each avatar entry %>
<%= for entry <- @uploads.avatar.entries do %>
  <article class="upload-entry">

    <figure>
      <%# Phoenix.LiveView.Helpers.live_img_preview/2 renders a client-side preview %>
      <%= live_img_preview entry %>
      <figcaption><%= entry.client_name %></figcaption>
    </figure>

    <%# entry.progress will update automatically for in-flight entries %>
    <progress value={entry.progress} max="100"> <%= entry.progress %>% </progress>

    <%# a regular click event whose handler will invoke Phoenix.LiveView.cancel_upload/3 %>
    <button phx-click="cancel-upload" phx-value-ref={entry.ref} aria-label="cancel">&times;</button>

    <%# Phoenix.LiveView.Helpers.upload_errors/2 returns a list of error atoms %>
    <%= for err <- upload_errors(@uploads.avatar, entry) do %>
      <p class="alert alert-danger"><%= error_to_string(err) %></p>
    <% end %>

  </article>
<% end %>

<%# Phoenix.LiveView.Helpers.upload_errors/1 returns a list of error atoms %>
<%= for err <- upload_errors(@uploads.avatar) do %>
  <p class="alert alert-danger"><%= error_to_string(err) %></p>
<% end %>

</section>

예제의 섹션 요소는 :avatar 업로드를 위한 phx-drop-target 역할을 합니다. 사용자는 파일 입력과 상호 작용하거나 요소 위에 파일을 놓아 새 항목을 추가할 수 있습니다.

업로드 항목은 파일이 양식 입력에 추가될 때 생성되며 업로드가 성공적으로 완료된 후 사용될 때까지 각각 존재합니다.

Entry validation

유효성 검사는 allow_upload/3에 지정된 조건에 따라 자동으로 발생하지만 앞서 언급했듯이 유효성 검사를 수행하려면 양식에 phx-change를 바인딩해야 합니다. 따라서 최소한 최소한의 콜백을 구현해야 합니다.

@impl Phoenix.LiveView
def handle_event("validate", _params, socket) do
  {:noreply, socket}
end

allow_upload/3 사양과 일치하지 않는 파일 항목에는 오류가 포함됩니다. Phoenix.LiveView.Helpers.upload_errors/2 및 자체 도우미 함수를 사용하여 친숙한 오류 메시지를 렌더링합니다.

def error_to_string(:too_large), do: "Too large"
def error_to_string(:not_accepted), do: "You have selected an unacceptable file type"

모든 항목에 영향을 주는 오류 메시지의 경우 Phoenix.LiveView.Helpers.upload_errors/1 및 자체 도우미 함수를 사용하여 친숙한 오류 메시지를 렌더링합니다.

def error_to_string(:too_many_files), do: "You have selected too many files"

Cancel an entry

업로드 항목은 프로그래밍 방식으로 또는 사용자 작업의 결과로 취소될 수도 있습니다. 예를 들어 위의 템플릿에서 클릭 이벤트를 처리하려면 다음을 수행할 수 있습니다.

@impl Phoenix.LiveView
def handle_event("cancel-upload", %{"ref" => ref}, socket) do
  {:noreply, cancel_upload(socket, :avatar, ref)}
end

Consume uploaded entries

최종 사용자가 live_file_input/2를 포함하는 양식을 제출하면 JavaScript 클라이언트는 양식의 phx-submit 이벤트에 대한 콜백을 호출하기 전에 먼저 파일을 업로드합니다.

phx-submit 이벤트에 대한 콜백 내에서 Phoenix.LiveView.consume_uploaded_entries/3 함수를 호출하여 완료된 업로드를 처리하고 관련 업로드 데이터를 양식 데이터와 함께 유지합니다.

@impl Phoenix.LiveView
def handle_event("save", _params, socket) do
  uploaded_files =
    consume_uploaded_entries(socket, :avatar, fn %{path: path}, _entry ->
      dest = Path.join([:code.priv_dir(:my_app), "static", "uploads", Path.basename(path)])
      # The `static/uploads` directory must exist for `File.cp!/2` to work.
      File.cp!(path, dest)
      {:ok, Routes.static_path(socket, "/uploads/#{Path.basename(dest)}")}
    end)

  {:noreply, update(socket, :uploaded_files, &(&1 ++ uploaded_files))}
end

참고: 클라이언트 메타데이터는 신뢰할 수 없지만 서버에 직접 업로드를 수행할 때 각 청크가 수신될 때 최대 파일 크기 유효성 검사가 시행됩니다.

Appendix A: UploadLive

이 가이드의 LiveView의 전체 예:

# lib/my_app_web/live/upload_live.ex
defmodule MyAppWeb.UploadLive do
  use MyAppWeb, :live_view

  @impl Phoenix.LiveView
  def mount(_params, _session, socket) do
    {:ok,
    socket
    |> assign(:uploaded_files, [])
    |> allow_upload(:avatar, accept: ~w(.jpg .jpeg), max_entries: 2)}
  end

  @impl Phoenix.LiveView
  def handle_event("validate", _params, socket) do
    {:noreply, socket}
  end

  @impl Phoenix.LiveView
  def handle_event("cancel-upload", %{"ref" => ref}, socket) do
    {:noreply, cancel_upload(socket, :avatar, ref)}
  end

  @impl Phoenix.LiveView
  def handle_event("save", _params, socket) do
    uploaded_files =
      consume_uploaded_entries(socket, :avatar, fn %{path: path}, _entry ->
        dest = Path.join([:code.priv_dir(:my_app), "static", "uploads", Path.basename(path)])
        File.cp!(path, dest)
        {:ok, Routes.static_path(socket, "/uploads/#{Path.basename(dest)}")}
      end)

    {:noreply, update(socket, :uploaded_files, &(&1 ++ uploaded_files))}
  end

  defp error_to_string(:too_large), do: "Too large"
  defp error_to_string(:too_many_files), do: "You have selected too many files"
  defp error_to_string(:not_accepted), do: "You have selected an unacceptable file type"
end
profile
asdasdasdasd

0개의 댓글