Ole Spaarmann
Ole Spaarmann

Reputation: 16761

Elixir Arc: File is uploaded and processed but not stored in database

I'm using elixirs Arc with Ecto and Amazon S3 to store files that I have previously downloaded. Everything seems to work, they end up on S3. But nothing is stored in my database. So if I try to generate an URL I always just get the default url back.

This is how I store a file:

iex > user = Repo.get(User, 3)
iex > Avatar.store({"/tmp/my_file.png", user})
{:ok, "my_file.png"}

But the user.avatar field is still nil.

My user module:

defmodule MyApp.User do
  use MyApp.Web, :model
  use Arc.Ecto.Schema

  alias MyApp.Repo

  schema "users" do
    field :name, :string
    field :email, :string
    field :avatar, MyApp.Avatar.Type    
    embeds_many :billing_emails, MyApp.BillingEmail
    embeds_many :addresses, MyApp.Address
    timestamps
  end

  @required_fields ~w(name email)
  @optional_fields ~w(avatar)

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> cast_embed(:billing_emails)
    |> cast_embed(:addresses)
    |> validate_required([:name, :email])
    |> validate_format(:email, ~r/@/)
    |> unique_constraint(:email)
    |> cast_attachments(params, [:avatar])
  end

end

The Avatar uploader:

defmodule MyApp.Avatar do
  use Arc.Definition

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

  @acl :public_read

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

  # Whitelist file extensions:
  def validate({file, _}) do
    ~w(.jpg .jpeg .gif .png) |> Enum.member?(Path.extname(file.file_name))
  end

  # Define a thumbnail transformation:
  def transform(:thumb, _) do
    {:convert, "-strip -thumbnail 250x250^ -gravity center -extent 250x250 -format png", :png}
  end

  def transform(:original, _) do
    {:convert, "-format png", :png}
  end

  def filename(version,  {file, scope}), do: "#{version}-#{file.file_name}"

  # Override the storage directory:
  def storage_dir(version, {file, scope}) do
    "uploads/user/avatars/#{scope.id}"
  end

  # Provide a default URL if there hasn't been a file uploaded
  def default_url(version, scope) do
    "/images/avatars/default_#{version}.png"
  end

end

Upvotes: 0

Views: 1379

Answers (1)

Ole Spaarmann
Ole Spaarmann

Reputation: 16761

So the answer to this is twofold. First of all I had to remove avatar from my optional_fields in my User Module.

@optional_fields ~w()

Second you don't call Avatar.store directly but use a changeset.

avatar_params = %{avatar: "/tmp/my_file.jpg"}
user = Repo.get(User, 1)
avatar_changeset = User.changeset(user, avatar_params)
Repo.update(avatar_changeset)

Edit: In a more Elixir way:

avatar_params = %{avatar: "/tmp/my_file.jpg"}
Repo.get(User, 1) |> User.changeset(avatar_params) |> Repo.update()

Upvotes: 2

Related Questions