【问题标题】:param structs in Phoenix controllersPhoenix 控制器中的参数结构
【发布时间】:2017-04-24 16:46:24
【问题描述】:

我在 Phoenix 创建了一个简单的“Thing”ecto 模型。 “图像”是一个字符串,它引用了上传的文件。当上传的图像到达控制器时,我想将图像结构转换为字符串(为了这个示例,我们将其保存为“foo123”)

#/web/models/thing.ex

defmodule MyApp.Thing do
  use MyApp.Web, :model

  schema "comics" do
    field :number, :integer
    field :image, :string

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:number, :image])
    |> validate_required([:number, :image])
  end
end

#/web/controllers/thing_controller.ex

  def create(conn, %{"thing" => thing_params}) do
    IO.inspect thing_params

    changeset = Thing.changeset(%Thing{}, thing_params) 
       |> change("image", "foo123") ### something like this

    case Repo.insert!(changeset) do
      {:ok, _thing} ->
        conn
        |> put_flash(:info, "Thing created successfully.")
        |> redirect(to: thing_path(conn, :index))
      {:error, changeset} ->
        conn
        |> put_flash(:error, "Something went wrong")
        |> redirect(to: thing_path(conn, :index))
    end
  end

这不起作用,我仍然需要将%{"image" => %Plug.Upload{content_type: "image/jpeg",... 转换为“foo123”,但这是在哪里发生的呢?在变更集分配中?在def create()的参数中?

另外,我怎样才能IO.inspect 只是 param 结构的某个成员? (比如IO.inspect thing_params.image

【问题讨论】:

  • 最后一个问题可以IO.inspect thing_params["image"].image 不起作用,因为键是字符串,而不是原子。
  • 对于主要问题,您可以使用|> put_change(:image, "foo123"),但这不会重新运行Thing.changeset/2 的验证。我不确定手动处理文件上传的最佳方式是什么,所以我只是在这里发表评论而不是发布答案。
  • @Dogbert 这实际上是我正在寻找的答案。 put_change() 去哪儿了?当我运行changeset = Thing.changeset(%Thing{}, thing_params) |> put_change(:image, "foo123") 时,我得到** (CompileError) web/controllers/thing_controller.ex:19: undefined function put_change/3。这是放错地方了吗,还是我忘记在控制器中导入一些东西?
  • @Dogbert:这正是我想要的,谢谢。如果您想发布此内容,我会将其标记为正确。 ` changeset = Thing.changeset(%Comic{}, comic_params) |> Ecto.Changeset.put_change(:image, "foo123")`

标签: parameters elixir phoenix-framework ecto changeset


【解决方案1】:

当多部分到达您的控制器时,Plug.Upload 已经处理了下载并将其放入临时文件中。该文件名可以在您的 %Plug.Upload{} 结构中找到。您可以将该文件复制到一个永久位置,选择您想要使用的任何名称。然后,您可以将该名称放入图像字段或架构中。您必须在控制器中执行此操作,因为请求结束后文件会被删除。

我建议你看看ArchEcto。它在处理文件附件方面做得很好,并提供了很好的 API 用于将 url 放入您的视图中。

您还可以插入图像处理来创建缩略图等。这是我处理图像和视频上传的一些代码。对于图像,我保存了完整的分辨率和海报大小的视图(用于在聊天应用程序中显示)。对于视频,我创建了一张海报图片,用于在聊天应用中显示视频。

defmodule UcxChat.File do
  use Arc.Definition

  # Include ecto support (requires package arc_ecto installed):
  use Arc.Ecto.Definition
  require Logger

  def __storage, do: Arc.Storage.Local

  @versions [:original, :poster]

  @acl :public_read

  # To add a thumbnail version:
  # @versions [:original, :thumb]

  # Whitelist file extensions:
  def validate({file, _}) do
    ~w(.jpg .jpeg .gif .png .txt .text .doc .pdf .wav .mp3 .mp4 .mov .m4a .xls) |> Enum.member?(Path.extname(file.file_name))
  end

  def transform(:poster, {_, %{type: "video" <> _}}) do
    {:ffmpeg, fn(input, output) ->
      "-i #{input} -f image2 -ss 00:00:01.00 -vframes 1 -vf scale=-1:200 #{output}" end, :jpg}
  end
  def transform(:poster, {_, %{type: "image" <> _}}) do
    {:convert, "-strip -resize @80000 -format png", :png}
  end

  def filename(:poster, _params) do
    :poster
  end
  def filename(_version, {_, %{file_name: file_name}}) do
    String.replace(file_name, ~r(\.[^/.]+$), "")
  end
  def filename(_version, %{file_name: file_name}) do
    file_name
  end

  def storage_dir(_version, {_file, scope}) do
    storage_dir(scope)
  end
  def storage_dir(scope) do
    path = "priv/static/uploads/#{scope.message_id}"
    if UcxChat.env == :prod do
      Path.join(Application.app_dir(:ucx_chat), path)
    else
      path
    end
  end

end

即使你不想使用这个包,它也可能会给你一些关于如何扮演你自己的角色的想法。

【讨论】:

  • 谢谢史蒂夫!我已经尝试过 arc_ecto 并且我喜欢它,但我实际上使用的是 ex_aws,因为我正在使用 Heroku 进行部署并且必须使用 S3 存储桶。即使我计划完全外包文件托管,arc_ecto 是否仍有选择?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-03-28
相关资源
最近更新 更多