我在Phoenix LiveView中有一个包含file_input的表单。我想使用它来允许用户上传图像。我在理解表单发送给后端的内容以及如何使用它方面遇到困难。如documentation中所述,我期望图像文件的%Plug.Upload {}表示形式,但我只是得到"[object File]"

请注意,我没有使用变更集来支持表单,因为我没有使用Ecto:

<%= f = form_for :post, "#", phx_submit: :create_post, phx_change: :image_attach, multipart: true %>
  <%= hidden_input f, :user_id, value: @current_user.account_id %>
  <%= textarea f, :message, class: "social-post-box", placeholder: "Something on your mind?" %>
  <div class="post-submit-container">
    <%= submit "Post", class: "post-submit" %>
    <label for="post_image" class="post-submit-image"></label
    <%= file_input f, :image %
  </div>
</form>

我在LiveView模块中有一个处理程序来处理提交的表单,当我检查图像上传时,我看到了"[object File]"
def handle_event("create_post", %{"post" => post_params}, socket) do
  IO.inspect post_params["image"]
  {:noreply, socket}
end

我尝试在此位置 pry 动,以便可以运行i post_params["image"],它说明该对象是一个位串,即只是一个二进制文件。因此,它实际上只是文本“[object File]”,甚至根本没有文件吗?

我收到的表格是什么?为什么不是%Plug.Upload{}?我如何实现将图像上传保存到本地文件系统的目标?

最佳答案

正如@sbacaro指出的那样,LiveView表单尚不支持文件上传。

有关此的更多信息:

  • https://github.com/phoenixframework/phoenix_live_view/issues/104
  • https://elixirforum.com/t/elixirconf-2019-how-liveview-handles-file-uploads-gary-rennie/25092

  • 我实现了Javascript解决方法,无需刷新页面即可手动发送表单(以便LiveView的其他部分继续正常运行)。

    但Phoenix在LiveViews中处理CSRF token 的方式也存在问题。事实证明,当套接字从客户端连接时,LiveView会创建一个新 token ,并且监听来自表单的POST的 Controller 将无法识别此 token 。要解决此问题,您需要手动将 token 传递到LiveView中。

    总体而言,此解决方法可以正常工作,但我希望将来有一天可以在这里指出文件上传已在LiveViews中获得支持,并且可以更轻松地共享。

    我的表格现在看起来像这样。请注意csrf token 的手动规范:
    <%= f = form_for :post, Routes.profile_path(UdsWeb.Endpoint, :post_social, @current_user.username), [phx_change: :image_attach, multipart: true, id: "social-timeline-form", csrf_token: @csrf_token] %>
      <%= hidden_input f, :user_id, value: @current_user.account_id %>
      <%= textarea f, :message, class: "social-post-box", placeholder: "Something on your mind?" %>
      <div class="post-submit-container">
        <%= submit "Post", class: "post-submit" %>
        <label for="post_image" class="post-submit-image"></label>
        <%= file_input f, :image %>
      </div>
    </form>
    

    我从普通的eex模板中渲染LiveView。请注意,我在此处手动指定csrf token :
    <%= Phoenix.LiveView.live_render(@conn, UdsWeb.ProfileTimelineLive, session: %{current_user: @current_user, csrf_token: Phoenix.Controller.get_csrf_token()}, container: {:div, class: "feed"}) %>
    

    时间轴模块具有安装功能,可将csrf token 加载到套接字分配中:
    def mount(%{current_user: current_user, csrf_token: csrf_token}, socket) do
      {:ok, assign(socket, current_user: current_user, csrf_token: csrf_token)}
    end
    

    手动控制表单提交的JS并不是很特别,但是这里是:
    function handleSocialTimelinePost(e) {
      e.preventDefault();
      let form = document.querySelector("#social-timeline-form");
      let formData = new FormData(form);
      let username = formData.get("post[username]");
      let request = new XMLHttpRequest();
      request.open("POST", `/profile/${username}`);
      request.send(formData);
    }
    
    document.querySelector("#social-timeline-form button.post-submit").onclick = handleSocialTimelinePost;
    

    关于elixir - Phoenix LiveView上的表单中的file_input为什么不返回%Plug.Upload {}?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59759281/

    10-13 06:13