피닉스 라이브뷰 업로드

43600 단어 elixirphoenixliveview
안녕하세요! 만나서 반가워요.
이 게시물은 저에게 첫 번째 게시물입니다.

소개



Phoenix LiveView 0.15 지원 live uploads .


  • 나는 그것을 시도한다.

    라이브 업로드를 기대하고 있었습니다.
    리뷰에 참여했습니다.
    내 제안(댓글만!)이 적용됩니다.
  • Update lib/phoenix_live_view/helpers.ex
  • Update lib/phoenix_live_view.ex
  • Update guides/server/uploads.md

  • 데모


  • https://elixir-is-beautiful.torifuku-kaiou.tokyo/pictures
  • 데모 사이트를 구축합니다.
  • 부담없이 이용해주세요.
  • 이 서버 사양이 별로...
  • 언젠가는 멈출 수 있습니다.
  • 죄송합니다...


  • 깃허브


  • 모든 소스 코드는 here 입니다.

  • 빌드 🚀🚀🚀




    $ mix phx.new gallery --live
    $ cd gallery
    $ mix ecto.create
    


  • 변경mix.exs

  •        {:phoenix_ecto, "~> 4.1"},
           {:ecto_sql, "~> 3.4"},
           {:postgrex, ">= 0.0.0"},
    -      {:phoenix_live_view, "~> 0.14.6"},
    +      {:phoenix_live_view, "~> 0.15.0", override: true},
           {:floki, ">= 0.27.0", only: :test},
           {:phoenix_html, "~> 2.11"},
           {:phoenix_live_reload, "~> 1.2", only: :dev},
    


  • mix deps.get

  • $ mix deps.get
    


  • mix phx.gen.live

  • $ mix phx.gen.live Art Picture pictures message
    


  • 그런 다음 코드를 추가, 제거, 변경합니다.

  • priv/repo/migrations/20201122051151_create_pictures.exs




    defmodule Gallery.Repo.Migrations.CreatePictures do
      use Ecto.Migration
    
      def change do
        create table(:pictures) do
          add :url, :string, null: false
    
          timestamps()
        end
      end
    end
    


    lib/gallery_web/live/picture_live/form_component.ex


  • allow_upload
  • cancel_upload
  • uploaded_entries

  • consume_uploaded_entries

  • defmodule GalleryWeb.PictureLive.FormComponent do
      use GalleryWeb, :live_component
    
      alias Gallery.Art
      alias Gallery.Art.Picture
    
      @impl true
      def mount(socket) do
        {:ok, allow_upload(socket, :photo, accept: ~w(.png .jpg .jpeg))}
      end
    
      @impl true
      def update(%{picture: picture} = assigns, socket) do
        changeset = Art.change_picture(picture)
    
        {:ok,
         socket
         |> assign(assigns)
         |> assign(:changeset, changeset)}
      end
    
      @impl true
      def handle_event("validate", _params, socket) do
        {:noreply, socket}
      end
    
      def handle_event("save", _params, socket) do
        picture = put_photo_url(socket, %Picture{})
    
        case Art.create_picture(picture, %{}, &consume_photo(socket, &1)) do
          {:ok, _picture} ->
            {:noreply,
             socket
             |> put_flash(:info, "Picture created successfully")
             |> push_redirect(to: socket.assigns.return_to)}
    
          {:error, %Ecto.Changeset{} = changeset} ->
            {:noreply, assign(socket, changeset: changeset)}
        end
      end
    
      def handle_event("cancel-entry", %{"ref" => ref}, socket) do
        {:noreply, cancel_upload(socket, :photo, ref)}
      end
    
      defp ext(entry) do
        [ext | _] = MIME.extensions(entry.client_type)
        ext
      end
    
      defp put_photo_url(socket, %Picture{} = picture) do
        {completed, []} = uploaded_entries(socket, :photo)
    
        urls =
          for entry <- completed do
            Routes.static_path(socket, "/uploads/#{entry.uuid}.#{ext(entry)}")
          end
    
        url = Enum.at(urls, 0)
    
        %Picture{picture | url: url}
      end
    
      def consume_photo(socket, %Picture{} = picture) do
        consume_uploaded_entries(socket, :photo, fn meta, entry ->
          dest = Path.join("priv/static/uploads", "#{entry.uuid}.#{ext(entry)}")
          File.cp!(meta.path, dest)
        end)
    
        {:ok, picture}
      end
    end
    


    lib/gallery_web/live/picture_live/form_component.html.leex


  • live_file_input

  • live_img_preview

  • <h2><%= @title %></h2>
    
    <%= f = form_for @changeset, "#",
      id: "picture-form",
      phx_target: @myself,
      phx_change: "validate",
      phx_submit: "save" %>
    
      <%= for {_ref, msg} <- @uploads.photo.errors do %>
        <p class="alert alert-danger"><%= Phoenix.Naming.humanize(msg) %></p>
      <% end %>
    
      <%= live_file_input @uploads.photo %>
    
      <%= for entry <- @uploads.photo.entries do %>
        <div class="row">
          <div class="column">
            <%= live_img_preview entry, height: 80 %>
          </div>
          <div class="column">
            <progress max="100" value="<%= entry.progress %>" />
          </div>
          <div class="column">
            <a href="#" phx-click="cancel-entry" phx-value-ref="<%= entry.ref %>"
               phx-target="<%= @myself %>">
              cancel
            </a>
          </div>
        </div>
      <% end %>
    
      <%= submit "Save", phx_disable_with: "Saving..." %>
    </form>
    


    라이브러리/갤러리/art.ex




    defmodule Gallery.Art do
      @moduledoc """
      The Art context.
      """
    
      import Ecto.Query, warn: false
      alias Gallery.Repo
    
      alias Gallery.Art.Picture
    
      @doc """
      Returns the list of pictures.
    
      ## Examples
    
          iex> list_pictures()
          [%Picture{}, ...]
    
      """
      def list_pictures do
        Repo.all(
          from p in Picture,
            order_by: [desc: p.inserted_at]
        )
      end
    
      def create_picture(picture, attrs \\ %{}, after_save) do
        picture
        |> Picture.changeset(attrs)
        |> Repo.insert()
        |> after_save(after_save)
      end
    
      defp after_save({:ok, picture}, func) do
        {:ok, _picture} = func.(picture)
      end
    
      defp after_save(error, _func), do: error
    
      @doc """
      Returns an `%Ecto.Changeset{}` for tracking picture changes.
    
      ## Examples
    
          iex> change_picture(picture)
          %Ecto.Changeset{data: %Picture{}}
    
      """
      def change_picture(%Picture{} = picture, attrs \\ %{}) do
        Picture.changeset(picture, attrs)
      end
    end
    


    lib/gallery/art/picture.ex




    defmodule Gallery.Art.Picture do
      use Ecto.Schema
      import Ecto.Changeset
    
      schema "pictures" do
        field :url, :string
    
        timestamps()
      end
    
      @doc false
      def changeset(picture, attrs) do
        picture
        |> cast(attrs, [:url])
        |> validate_required([:url])
      end
    end
    


    lib/gallery_web/live/picture_live/index.ex




    defmodule GalleryWeb.PictureLive.Index do
      use GalleryWeb, :live_view
    
      alias Gallery.Art
      alias Gallery.Art.Picture
    
      @impl true
      def mount(_params, _session, socket) do
        {:ok, assign(socket, list_of_pictures: list_of_pictures())}
      end
    
      @impl true
      def handle_params(params, _url, socket) do
        {:noreply, apply_action(socket, socket.assigns.live_action, params)}
      end
    
      defp apply_action(socket, :new, _params) do
        socket
        |> assign(:page_title, "New Picture")
        |> assign(:picture, %Picture{})
      end
    
      defp apply_action(socket, :index, _params) do
        socket
        |> assign(:page_title, "Listing Pictures")
        |> assign(:picture, nil)
      end
    
      defp list_of_pictures do
        Art.list_pictures() |> Enum.chunk_every(3)
      end
    end
    


    lib/gallery_web/live/picture_live/index.html.leex




    <h1>Listing Pictures</h1>
    
    <%= if @live_action in [:new] do %>
      <%= live_modal @socket, GalleryWeb.PictureLive.FormComponent,
        id: @picture.id || :new,
        title: @page_title,
        action: @live_action,
        picture: @picture,
        return_to: Routes.picture_index_path(@socket, :index) %>
    <% end %>
    
    <span><%= live_patch "New Picture", to: Routes.picture_index_path(@socket, :new) %></span>
    
    <table>
      <thead>
        <tr>
          <th></th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      <tbody id="pictures">
        <%= for pictures <- @list_of_pictures do %>
          <tr>
            <%= for picture <- pictures do %>
              <td><img src="<%= picture.url %>" height="150" /></td>
            <% end %>
          </tr>
        <% end %>
      </tbody>
    </table>
    


    lib/gallery_web.ex




           # Import LiveView helpers (live_render, live_component, live_patch, etc)
           import Phoenix.LiveView.Helpers
    +      import GalleryWeb.LiveHelpers
    


    lib/gallery_web/live/live_helpers.ex




    defmodule GalleryWeb.LiveHelpers do
      import Phoenix.LiveView.Helpers
    
      @doc """
      Renders a component inside the `GalleryWeb.ModalComponent` component.
    
      The rendered modal receives a `:return_to` option to properly update
      the URL when the modal is closed.
    
      ## Examples
    
          <%= live_modal @socket, GalleryWeb.PictureLive.FormComponent,
            id: @picture.id || :new,
            action: @live_action,
            picture: @picture,
            return_to: Routes.picture_index_path(@socket, :index) %>
      """
      def live_modal(socket, component, opts) do
        path = Keyword.fetch!(opts, :return_to)
        modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
        live_component(socket, GalleryWeb.ModalComponent, modal_opts)
      end
    end
    


    lib/gallery_web/live/modal_component.ex




    defmodule GalleryWeb.ModalComponent do
      use GalleryWeb, :live_component
    
      @impl true
      def render(assigns) do
        ~L"""
        <div id="<%= @id %>" class="phx-modal"
          phx-capture-click="close"
          phx-window-keydown="close"
          phx-key="escape"
          phx-target="#<%= @id %>"
          phx-page-loading>
    
          <div class="phx-modal-content">
            <%= live_patch raw("&times;"), to: @return_to, class: "phx-modal-close" %>
            <%= live_component @socket, @component, @opts %>
          </div>
        </div>
        """
      end
    
      @impl true
      def handle_event("close", _, socket) do
        {:noreply, push_patch(socket, to: socket.assigns.return_to)}
      end
    end
    


    라이브러리/갤러리_웹/라우터.ex




         pipe_through :browser
    
         live "/", PageLive, :index
    +    live "/pictures", PictureLive.Index, :index
    +    live "/pictures/new", PictureLive.Index, :new
       end
    


    구성/dev.exs




     config :gallery, GalleryWeb.Endpoint,
       live_reload: [
         patterns: [
    -      ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
    +      ~r"priv/static/[^uploads].*(js|css|png|jpeg|jpg|gif|svg)$",
           ~r"priv/gettext/.*(po)$",
           ~r"lib/gallery_web/(live|views)/.*(ex)$",
           ~r"lib/gallery_web/templates/.*(eex)$"
    


    lib/gallery_web/endpoint.ex




         at: "/",
         from: :gallery,
         gzip: false,
    -    only: ~w(css fonts images js favicon.ico robots.txt)
    +    only: ~w(css fonts images js favicon.ico robots.txt uploads)
    


    운영!!!




    $ mkdir priv/static/uploads
    $ mix ecto.migrate
    $ mix phx.server
    


    방문: http://localhost:4000/pictures

    참조


  • Phoenix LiveView Uploads Deep Dive
  • phoenix_live_view/guides/server/uploads.md

  • 마무리!


  • 즐감 Elixir !!!
  • IEx에서 아래 스니펫을 실행하십시오.

  • iex> [87, 101, 32, 97, 114, 101, 32, 116, 104, 101, 32, 65, 108, 99, 104, 101, 109, 105, 115, 116, 115, 44, 32, 109, 121, 32, 102, 114, 105, 101, 110, 100, 115, 33]
    


  • 감사합니다!
  • 좋은 웹페이지 즐겨찾기