Giter VIP home page Giter VIP logo

arc_ecto's Introduction

Arc.Ecto

Arc.Ecto provides an integration with Arc and Ecto.

Installation

Add the latest stable release to your mix.exs file:

defp deps do
  [
    {:arc_ecto, "~> 0.11.3"}
  ]
end

Then run mix deps.get in your shell to fetch the dependencies.

Usage

Add Arc.Ecto.Definition

Add a second using macro use Arc.Ecto.Definition to the top of your Arc definitions.

defmodule MyApp.Avatar do
  use Arc.Definition
  use Arc.Ecto.Definition

  # ...
end

This provides a set of functions to ease integration with Arc and Ecto. In particular:

  • Definition of a custom Ecto Type responsible for storing the images.
  • Url generation with a cache-busting timestamp query parameter

Add a string column to your schema

Arc attachments should be stored in a string column, with a name indicative of the attachment.

create table :users do
  add :avatar, :string
end

Add your attachment to your Ecto Schema

Add a using statement use Arc.Ecto.Schema to the top of your ecto schema, and specify the type of the column in your schema as MyApp.Avatar.Type.

Attachments can subsequently be passed to Arc's storage though a Changeset cast_attachments/3 function, following the syntax of cast/3

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

  schema "users" do
    field :name,   :string
    field :avatar, MyApp.Avatar.Type
  end

  @doc """
  Creates a changeset based on the `data` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(user, params \\ :invalid) do
    user
    |> cast(params, [:name])
    |> cast_attachments(params, [:avatar])
    |> validate_required([:name, :avatar])
  end
end

Save your attachments as normal through your controller

  @doc """
  Given params of:

  %{
    "id" => 1,
    "user" => %{
      "avatar" => %Plug.Upload{
                    content_type: "image/png",
                    filename: "selfie.png",
                    path: "/var/folders/q0/dg42x390000gp/T//plug-1434/multipart-765369-5"}
    }
  }

  """
  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Repo.get(User, id)
    changeset = User.changeset(user, user_params)

    if changeset.valid? do
      Repo.update(changeset)

      conn
      |> put_flash(:info, "User updated successfully.")
      |> redirect(to: user_path(conn, :index))
    else
      render conn, "edit.html", user: user, changeset: changeset
    end
  end

Retrieve the serialized url

Both public and signed urls will include the timestamp for cache busting, and are retrieved the exact same way as using Arc directly.

  user = Repo.get(User, 1)

  # To receive a single rendition:
  MyApp.Avatar.url({user.avatar, user}, :thumb)
    #=> "https://bucket.s3.amazonaws.com/uploads/avatars/1/thumb.png?v=63601457477"

  # To receive all renditions:
  MyApp.Avatar.urls({user.avatar, user})
    #=> %{original: "https://.../original.png?v=1234", thumb: "https://.../thumb.png?v=1234"}

  # To receive a signed url:
  MyApp.Avatar.url({user.avatar, user}, signed: true)
  MyApp.Avatar.url({user.avatar, user}, :thumb, signed: true)

License

Copyright 2015 Sean Stavropoulos

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

arc_ecto's People

Contributors

almirsarajcic avatar alxgnon avatar anzaika avatar asiniy avatar azhi avatar bcardarella avatar dbernheisel avatar drapergeek avatar duijf avatar ejpcmac avatar ericmj avatar fladson avatar gktoken avatar graydiant avatar joebew42 avatar krbullock avatar merqlove avatar mikker avatar mpugach avatar neuroradiology avatar richeterre avatar sobolevn avatar stavro avatar vursen avatar zuraguerra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arc_ecto's Issues

Timout when transforming image

Whenever I try to convert images to something a bit larger than 640px or so I get a timeout. I have tried to increase the timeout time to up to 2 minutes but it doesn't complete before that either.

The same problem happens on both my local machine and on Heroku.

I guess this problem has something to do with Image Magick but I basically do the same conversions using paperclip on Rails without any problems.

Have anybody else experienced the same problem? Are there workarounds?

This is what my definition looks like:

defmodule Succinct.FeaturedImage do
  use Arc.Definition
  use Arc.Ecto.Definition

  @acl :public_read
  @versions [:original, :thumb, :small]

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

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

  def transform(:small, _) do
    {:convert, "-strip -resize 640x640> -quality 80", :jpg}
  end

  def transform(:large, _) do
    {:convert, "-strip -resize 1400x1400> -quality 60 -format jpg", :jpg}
  end

end

Changeset doesn't track changes. Changes are nil.

Hey,

I'm using arc 0.8.0 and arc_ecto 0.7.0. I am encountering a really strange behavior. At some point uploading local files was broken (with a previous version of arc). I decided to upgrade in a hope to fix the issue. I now ran into a different problem: When I pass a path to a local file, a remote file or an Upload Plug into the changeset no changes are being tracked. And it is driving me insane for hours.

I created a new Phoenix app to more easily identify the problem. I can upload the code if that helps.

This is how I try to store an image (for an existing user with the ID 1).

user = Repo.get!(User, 1)
remote_path = "https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg"
user_params = %{avatar: remote_path}
avatar_changeset = User.avatar_changeset(user, user_params)

The resulting changeset looks like this:

#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #Image.User<>,
 valid?: true>

No changes are tracked. When I on the other hand (with the same setup) upload it without the changeset the file is persisted at S3. So at least the config seems to be correct:

Avatar.store({remote_path, user})

mix.exs

  defp deps do
    [{:phoenix, "~> 1.2.1"},
     {:phoenix_pubsub, "~> 1.0"},
     {:phoenix_ecto, "~> 3.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.6"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"},

     {:arc, "~> 0.8.0"},
     {:arc_ecto, "0.7.0"},
     {:ex_aws, "~> 1.1"}, 
     #{:httpoison, "0.11.0", override: true},  
     {:hackney, "1.6.5"}, 
     {:poison, "~> 2.1"},
     {:sweet_xml, "~> 0.6"}

   ]
  end

dev.exs

use Mix.Config

# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources.
config :image, Image.Endpoint,
  http: [port: 4000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  watchers: []


# Watch static and templates for browser reloading.
config :image, Image.Endpoint,
  live_reload: [
    patterns: [
      ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
      ~r{priv/gettext/.*(po)$},
      ~r{web/views/.*(ex)$},
      ~r{web/templates/.*(eex)$}
    ]
  ]

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20

# Configure your database
config :image, Image.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: System.get_env("POSTGRES_USERNAME"),
  password: System.get_env("POSTGRES_PASSWORD"),
  database: System.get_env("POSTGRES_DATABASE_NAME"),
  hostname: System.get_env("POSTGRES_HOSTNAME"),
  pool_size: 10

config :ex_aws,
  access_key_id: [System.get_env("AWS_ACCESS_KEY_ID"), :instance_role],
  secret_access_key: [System.get_env("AWS_SECRET_ACCESS_KEY"), :instance_role],
  s3: [
    scheme: "https://",
    host: "s3.eu-central-1.amazonaws.com",
    region: "eu-central-1"
  ]

config :ex_aws, :hackney_opts,
  recv_timeout: 300_000

config :arc,
  storage: Arc.Storage.S3,
  bucket: System.get_env("AWS_S3_BUCKET"),
  virtual_host: true,
  version_timeout: 15_000


/web/uploaders/avatar.exs


defmodule Image.Avatar do
  @moduledoc """
  Uploader to handle avatars of members (with Arc).
  """

  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, :medium, :thumb, :tiny]

  # 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(:original, _) do
    {:convert, "-format png -quality 85%", :png}
  end

  def transform(:medium, _) do
    {:convert, "-strip -thumbnail 600x600^ -gravity center -extent 600x600 -format png", :png}
  end

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

  def transform(:tiny, _) do
    {:convert, "-strip -thumbnail 100x100^ -gravity center -extent 100x100 -format png", :png}
  end

  # def __storage, do: Arc.Storage.S3

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

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

  def s3_object_headers(_version, {file, _scope}) do
    [content_type: Plug.MIME.path(file.file_name)] # for "image.png", would produce: "image/png"
  end

end

/web/models/user.exs

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

  schema "user" do
    field :name, :string
    field :avatar, Image.Avatar.Type

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(user, params \\ :invalid) do
    user
    |> cast(params, [:name])
    |> validate_required([:name])
  end
  
  # This is the changeset I'm using
  def avatar_changeset(model, params \\ :invalid) do
    model
    |> cast(params, [:name, :id])
    |> cast_attachments(params, [:avatar])
  end
end

(UndefinedFunctionError) function ExAws.S3.Upload.stream_file/1 is undefined (module ExAws.S3.Upload is not available)

Here is my output


{"image" => %Plug.Upload{content_type: "image/jpeg",
   filename: "17409815_1317417211684158_788963503_n.jpg",
   path: "/tmp/plug-1491/multipart-576910-531644-1"}}
[error] Task #PID<0.922.0> started from #PID<0.917.0> terminating
** (UndefinedFunctionError) function ExAws.S3.Upload.stream_file/1 is undefined (module ExAws.S3.Upload is not available)
    ExAws.S3.Upload.stream_file("/tmp/plug-1491/multipart-576910-531644-1")
    lib/arc/storage/s3.ex:55: Arc.Storage.S3.do_put/3
    (elixir) lib/task/supervised.ex:85: Task.Supervised.do_apply/2
    (elixir) lib/task/supervised.ex:36: Task.Supervised.reply/5
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Function: #Function<0.52315234/0 in Arc.Actions.Store.async_put_version/3>

Here is my code

defmodule BlogTest.Avatar do
  use Arc.Definition
  use Arc.Ecto.Definition



  @versions [:original]



  def filename(version, {_file, scope}) do
    "{scope.uuid}_#{version}"
  end
  
  # Override the storage directory:
  def storage_dir(version, {file, _scope}) do
    "uploads/user/avatars/#{5}"
  end


end

Files saved with double extension

when using a custom filename that include the actual name of the uploaded file, the extension is duplicated. Here is my code:

defmodule Scotchy.Image do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original, :thumb]

  def __storage, do: Arc.Storage.Local

  def filename(version,  {file, _scope}), do: "#{version}_#{file.file_name}"

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

  def storage_dir(_version, {_file, scope}) do
    "uploads/bottle_images/images/#{scope.id}"
  end

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

  def default_url(:thumb) do
    "https://placehold.it/350x400"
  end
end

which results in images named thumb_IMG_055.jpg.jpg. I know I can String.replace(file.file_name, ".jpg", "") but that seems hacky

Allow storage of based on external URLs?

@stavro

Is it possible to add an option to store the image based on an external URL, filename and scope? Def.store(%{filename: '....', path: 'blob:http://localhost:4000/sdfsfsfd.jpg'}, my_scope)

Currently even though there's a pattern match to accept this, it doesn't work because it uses File.exists? to check if the file is temp stored in Phoenix. Which this file isn't, so this returns an error, even though the URL is technically still valid:

  def new(%{filename: filename, path: path}) do
    case File.exists?(path) do
      true -> %Arc.File{path: path, file_name: filename}
      false -> {:error, :invalid_file_path}
    end
  end

I'd love to have support for storing images based on external source URL and a filename. This would be a huge convenience because users will no longer need to send FormData in order to generate the Plug.Upload struct.

PS. Thanks for the amazing library!

erlcloud dependency

Hi there,

I plan on using arc_ecto (arc itself builds fine) with the local file system (not s3 etc) and I'm having difficult installing it due to erlcloud, somewhere something is missing on my install and dependency resolution is not resolving it.
Am I missing something obvious?

==> lhttpc (compile)
==> jsx (compile)
==> erlcloud (compile)
Compiling src/erlcloud_xml.erl failed:
src/erlcloud_xml.erl:5: can't find include lib "xmerl/include/xmerl.hrl"
src/erlcloud_xml.erl:72: variable 'Value' is unbound
src/erlcloud_xml.erl:72: record xmlText undefined
src/erlcloud_xml.erl:73: record xmlElement undefined
src/erlcloud_xml.erl:74: variable 'Content' is unbound
src/erlcloud_xml.erl:80: variable 'Value' is unbound
src/erlcloud_xml.erl:80: record xmlAttribute undefined
src/erlcloud_xml.erl:86: record xmlText undefined
ERROR: compile failed while processing /home/anthony/Projects/peoplematchr_elixir/deps/erlcloud: rebar_abort
** (Mix) Could not compile dependency erlcloud, /home/anthony/.mix/rebar command failed. If you want to recompile this dependency, please run: mix deps.compile erlcloud

here's my deps in case it's me:-

  defp deps do
    [{:phoenix, "~> 1.0.2"},
     {:phoenix_ecto, "~> 1.1"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.1"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:cowboy, "~> 1.0"},
     {:factory_girl_elixir, "~> 0.1.1"},
     {:faker, "~> 0.5"},
     {:arc, "~> 0.1.2"},
     {:arc_ecto, "~> 0.2.0"}
     ]
  end

Upload via remote URL doesn't seem to work.

As per the codes, allow_paths: true option in cast_attachments passes the remote URL to arc for uploading.

But it doesn't seem to work when I tested.

Does anyone use latest arc_ecto with remote URL upload function?

Pass binary to `cast_attachments`

There's a referenced issue in arc but I think it's arc_ecto's responsibility so I create an issue here.

My requirement is uploading file as base64 encoded string to the server. It's common for small files and is easy to work with JSON API. After googling I found arc support binary, so I thought I can decode base64 to binary and save the file.

arc support passing binary like this Avatar.store(%{filename: "file.png", binary: binary}) but there's no way to pass binary in arc_ecto, The cast_attachments(%{filename: "file.png", binary: binary}) fails. I checked the source and it seems the arc_ecto only support Plug.Upload struct. Is there a way or workaround to achieve this?

Issues with atoms.

When i try to print the url for my model using the url function like this.

<%= Mobil.Logo.url({company.logo, company}, :thumb) %>

I get the following error

** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in IO.chardata_to_string/1
        (elixir) lib/io.ex:330: IO.chardata_to_string(:thumb)
        (elixir) lib/path.ex:474: Path.do_join/3
        (elixir) lib/path.ex:469: Path.join/2
        (mobil) web/uploaders/logo.ex:3: Mobil.Logo.url/3
        (mobil) web/templates/admin/company/index.html.eex:24: anonymous fn/3 in Mobil.Admin.CompanyView.index.html/1
        (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
        (mobil) web/templates/admin/company/index.html.eex:22: Mobil.Admin.CompanyView."index.html"/1
        (phoenix) lib/phoenix/view.ex:197: Phoenix.View.render_within/3

But if instead uses a string, like this

<%= Mobil.Logo.url({company.logo, company}, "thumb") %>

Then there is no problems.

This is also a problem when uploading images, where i had add to cast the file name to a string, as i got the somewhat same error without it.

lib/arc/actions/storage/local.ex, line: 9

{:ok, _} = File.copy(file.path, Path.join(destination_dir, file_name |> Atom.to_string))

This could be a Arc issue, however i'm using it with the arc_ecto module :-)

My stack

%{"arc": {:hex, :arc, "0.1.1"},
  "arc_ecto": {:hex, :arc_ecto, "0.1.2"},
  "comeonin": {:hex, :comeonin, "1.1.2"},
  "cowboy": {:hex, :cowboy, "1.0.2"},
  "cowlib": {:hex, :cowlib, "1.0.1"},
  "decimal": {:hex, :decimal, "1.1.0"},
  "ecto": {:hex, :ecto, "1.0.2"},
  "erlcloud": {:hex, :erlcloud, "0.9.2"},
  "fs": {:hex, :fs, "0.9.2"},
  "jsx": {:hex, :jsx, "2.1.1"},
  "lhttpc": {:hex, :lhttpc, "1.3.0"},
  "meck": {:hex, :meck, "0.8.3"},
  "phoenix": {:hex, :phoenix, "1.0.2"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "1.2.0"},
  "phoenix_html": {:hex, :phoenix_html, "2.2.0"},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.0"},
  "plug": {:hex, :plug, "1.0.0"},
  "poison": {:hex, :poison, "1.5.0"},
  "poolboy": {:hex, :poolboy, "1.5.1"},
  "postgrex": {:hex, :postgrex, "0.9.1"},
  "ranch": {:hex, :ranch, "1.1.0"}}

Would love to help, but my macro skills are not present yet :/

Can't access file/file path in changeset

What's the point of the cast_attatchements helper? I want to be able to do some stuff with the file in my changeset. Right now, purely by having my schema as field :picture, Pykture.PictureUploader.Type, it causes the changeset the process the upload to S3, and covert my :changes field in my changeset to a %{file_name: "name.jpg", updated_at: XXX}. I would have have params like: %{picture: "/some/path/name.jpg"}. I want to be able to use /some/path/name.jpg BEFORE I decide the upload the process my file, I thought that was what cast_attatchements did, and I could therefore process/upload my file at a later point in my changeset sequence. Is there anyway to do this? Right now there seems to be no difference between having cast_attatchements and not having it. Either way, my changeset will immediately upload/process my file, and convert the file to use the customize map.

How do I handle a nil value for Plug.Upload in params?

  • Elixir 1.2
  • Phoenix 1.2.1
  • arc: 0.6.0
  • arc_ecto 0.5.0

According to the Phoenix documentation, if no file is selected, Plug.Upload will not be present in the params map.

Finally, notice that when there is no data from the file input, we get neither the "photo" key nor a Plug.Upload struct. Here are the user_params from the log.

So if you submit a blank form, params[:user] will be nil. For example:

<%= form_for @changeset, @action, [multipart: true], fn f -> %>
  <%= file_input f, :avatar %>
  <%= submit "Submit" %>
<% end %>

Notice the absence of the "user" key.

%{"_csrf_token" => "...", "_method" => "put", "_utf8" => "โœ“"}

Which raises an error in the controller.

bad request to App.Account.AvatarController.update, no matching action clause to process request
  def update(conn, %{"user" => user_params}, current_user) do
    changeset = User.avatar_changeset(current_user, user_params)
    case Repo.update(changeset) do
      {:ok, _} ->
        redirect(conn, to: current_user_avatar_path(conn, :edit))
      {:error, changeset} ->
        render(conn, :edit, changeset: changeset, user: current_user)
    end

We can hack around the error by including a hidden field in the form, forcing params[:user] to exist.

<%= hidden_input f, :id %>

But Plug.Upload will still be missing from the params. Which means the changeset will be valid. Notice changes being an empty map.

#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #App.User<>,
 valid?: true>

So, my questions are this:

  1. What's the point of |> validate_required([:avatar]) given that Plug.Upload is only present with file data? To reach this point in the changeset, a field must be present. It seems redundant then to validate for presence.
  2. How do we validate the presence of avatar?

Here's my user schema:

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

  schema "users" do
    # ...
    field :avatar, App.AvatarUploader.Type
  end

  def avatar_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:avatar])
    |> cast_attachments(params, [:avatar])
    |> validate_required([:avatar])
  end

Am I missing something?

Issue when using exrm

Hello,

I have trouble with arc_ecto when using it with exrm release.
I have test the development version everything work fine.
It's seems like arc_ecto and ecto didn't get included in the release.
Could you recommend where should I look to fix any issues.

The error that I get is as follows -

02:56:13.493 request_id=pb76qtumnk6u9p7irjoketfv25p24tb9 [info] Sent 500 in 51ms 
02:56:13.508 [error] #PID<0.270.0> running Warehouse.Endpoint terminated
Server: warehouse.csweb.in.th:80 (http)
Request: POST /manage/companies/1/import
** (exit) an exception was raised:
    ** (UndefinedFunctionError) undefined function Arc.Ecto.Model.convert_params_to_binary/1 (module Arc.Ecto.Model is not available)
        Arc.Ecto.Model.convert_params_to_binary(%{"import" => %Plug.Upload{content_type: "application/pdf", filename: "application_form_original.th.kamolthip.pdf", path: "/tmp/plug-1455/multipart-72973-462679-1"}})

my mix.exs look like this

defmodule Warehouse.Mixfile do
  use Mix.Project

  def project do
    [app: :warehouse,
     version: "0.0.6",
     elixir: "~> 1.0",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     aliases: aliases,
     deps: deps]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [mod: {Warehouse, []},
     applications: [:phoenix, :phoenix_html, :cowboy, :logger, :gettext,
                    :phoenix_ecto, :postgrex, :comeonin]]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
  defp elixirc_paths(_),     do: ["lib", "web"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [{:phoenix, "~> 1.1.2"},
     {:phoenix_ecto, "~> 2.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.3"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.9"},
     {:comeonin, "~> 2.1"},
     {:arc_ecto, "~> 0.3.1"},
     {:exrm, "~> 0.19.9"},
     {:cowboy, "~> 1.0"}]
  end

  # Aliases are shortcut or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
     "ecto.reset": ["ecto.drop", "ecto.setup"]]
  end
end

my mix.lock looks like this

%{"arc": {:hex, :arc, "0.2.3"},
  "arc_ecto": {:hex, :arc_ecto, "0.3.1"},
  "bbmustache": {:hex, :bbmustache, "1.0.3"},
  "comeonin": {:hex, :comeonin, "2.1.0"},
  "conform": {:hex, :conform, "0.17.0"},
  "connection": {:hex, :connection, "1.0.2"},
  "cowboy": {:hex, :cowboy, "1.0.4"},
  "cowlib": {:hex, :cowlib, "1.0.2"},
  "db_connection": {:hex, :db_connection, "0.2.1"},
  "decimal": {:hex, :decimal, "1.1.1"},
  "ecto": {:hex, :ecto, "1.1.3"},
  "erlware_commons": {:hex, :erlware_commons, "0.15.0"},
  "exrm": {:hex, :exrm, "0.19.9"},
  "fs": {:hex, :fs, "0.9.2"},
  "getopt": {:hex, :getopt, "0.8.2"},
  "gettext": {:hex, :gettext, "0.9.0"},
  "neotoma": {:hex, :neotoma, "1.7.3"},
  "phoenix": {:hex, :phoenix, "1.1.4"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "2.0.1"},
  "phoenix_html": {:hex, :phoenix_html, "2.4.0"},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.3"},
  "plug": {:hex, :plug, "1.1.0"},
  "poison": {:hex, :poison, "1.5.2"},
  "poolboy": {:hex, :poolboy, "1.5.1"},
  "postgrex": {:hex, :postgrex, "0.11.0"},
  "providers": {:hex, :providers, "1.4.1"},
  "ranch": {:hex, :ranch, "1.2.1"},
  "relx": {:hex, :relx, "3.5.0"}}

Thank you for your help.

Is cast_attachments changing my params?

When using cast_attachments/4, Phoenix seem to choke, with the following error, while rendering my form.

Request: POST /admin/companies/6
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in Phoenix.HTML.Safe.Tuple.to_iodata/1
        (phoenix_html) lib/phoenix_html/safe.ex:72: Phoenix.HTML.Safe.Tuple.to_iodata({"sadfasdfasdf", %Mobil.Company{__meta__: #Ecto.Schema.Metadata<:loaded>, id: 6, inserted_at: #Ecto.DateTime<2015-09-09T15:12:24Z>, logo: %{file_name: "hรธstning.png", updated_at: #Ecto.DateTime<2015-09-09T15:12:24Z>}, name: nil, updated_at: #Ecto.DateTime<2015-09-09T15:12:24Z>}})
        (phoenix_html) lib/phoenix_html/tag.ex:66: anonymous fn/2 in Phoenix.HTML.Tag.tag_attrs/1
        (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
        (phoenix_html) lib/phoenix_html/tag.ex:35: Phoenix.HTML.Tag.tag/2
        (mobil) web/templates/admin/company/form.html.eex:15: anonymous fn/2 in Mobil.Admin.CompanyView.form.html/1
        (phoenix_html) lib/phoenix_html/form.ex:227: Phoenix.HTML.Form.form_for/4
        (mobil) web/templates/admin/company/form.html.eex:1: Mobil.Admin.CompanyView."form.html"/1
        (mobil) web/templates/admin/company/edit.html.eex:7: Mobil.Admin.CompanyView."edit.html"/1

As Phoenix tries to render

<%= text_input f, :name, class: "form-control" %>

Which suggest, that my name value was changed?

If i change it back to cast/4, then the problem is gone, but then i'm missing my image.

Asynchronous race condition problem

While trying to upload through cast_attachments/3, I do this in the controller:

  def update(conn, %{"id" => id, "artist" => artist_params}) do # this contains the file params too.
    artist = Repo.get!(Artist, id)
    changeset = Artist.changeset(artist, artist_params)
    case Repo.update(changeset) do
      {:ok, artist} ->
        conn
        |> put_flash(:info, "some message")
        |> redirect(to: somewhere)
      {:error, error} ->
        render(conn, "edit.html", artist: artist, changeset: changeset)
    end
  end

It always goes back to the edit page when cast_attachments/3 is called. When I turn it off, it works correctly. If you pry it in the case results, it will render on its own and the pry would still be available.

Why does this happen?

Feature Request: Add Image caching for when ecto changesets are invalid

Given I'm filling out a form for a model
When I'v selected an image
And I have some other attributes that will cause the changeset to be invalid
When I submit the form and it re-renders the edit view
Then I would like it if I didn't have to reselect the image and the previous selection was still set.

An example of how a simular ruby project carrierwave does this can be found here.
https://github.com/carrierwaveuploader/carrierwave#making-uploads-work-across-form-redisplays

No file stored or storage_dir scope == nil

I've user model with an avatar field similar to the example here: https://github.com/stavro/arc_ecto#add-your-attachment-to-your-ecto-model

No file is stored when a file is uploaded. When I change the required/optional fields to this:

  @required_fields ~w()
  @optional_fields ~w(name avatar) # <-- added avatar here

  @required_file_fields ~w()
  @optional_file_fields ~w(avatar)

A file is stored in uploads. When I change the def storage_dir(version, {file, scope}) do method to: "uploads/users/avatar/#{scope.id}" the scope field is empty. If I remove the avatar field from @optional_fields then the scope field is set, but no file is uploaded. I need both to work ๐Ÿ˜‰

Can you help me? Cheers!

One does not simply delete an Arc attachment

Let's say I want to delete an image attachment, but not replace it with a new one. I can't seem to figure out how to get that done.

I tried replacing the file field with a hidden field of same name and nil value or "" value:

<%= hidden_input f, :logo_image, value: "" %>

But that gives me:

no function clause matching in Arc.Actions.Store.store/2

When calling cast_attachments. I know there is an Arc.Actions.Delete, but I don't know how to invoke it.

If you lend me a hand I'd be happy to update the README to include this procedure.

Thanks in advance. ๐Ÿป

JSON Resource uploading

The example in the README.md walks you through uploading a file using a HTML <form>. How do you post a file and other attributes to a JSON api?

Store original file size and filename to Model

Is there any way to store original filename and file size on Repo Model ?
I want to calculate total uploaded file size per user, if it provided directly from database it would be very helpful.
Thanks
Dwi

Example of multiple uploads

Is multiple uploads supported? Something like:

  schema "users" do
    field :files, {:array, MyApp.File.Type}
  end

If yes, how can i create validation in this case?

Release?

Would you mind cutting a release with the newest changes? The 1.4 changes are holding us up from upgrading without running directly from GitHub.

`cast_attachments` with non-file parameters

I naively tried to just replace calls to cast with calls to cast_attachments, and this causes errors with any non-attachment parameters on my models. In essence, if you add a name field to the User model there will be errors reported on that field. Also, even getting to the new form was problematic if there were non-attachment parameters. I only got to the errors by hacking around a bit to get to the form first (otherwise the changeset created on new will throw an error re: :empty being an invalid dict.

Anyway, really like arc and I'm presently doing an ElixirSip on it :) Thanks!

Overridden filename not stored in the database

The below works to rename the file, but it does not rename what is stored in the database as the name of the file.

def filename(version, _) do
"#{UUID.uuid1()}"
end

The file name in the database remains the original file name, instead of the overridden version from the uuid above.

If the filename and what is stored in the db differ, the system is broken. Carrierwave updates the db with the new overridden file name. Is it possible to do the same with arc_ecto?

Image scope missing

I have a Site model and a SiteImages model.
:site has_many :site_images

When i upload the file, it goes into uploads/site_image/ instead of going into uploads/site_image/#{scope.id}

site_image_uploader.ex

defmodule Myapp.SiteImageUploader do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original]
  @acl :public_read

  @extension_whitelist ~w(.jpg .jpeg .gif .png)

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

  # Whitelist file extensions:
  def validate({file, _}) do
    file_extension = file.file_name |> Path.extname |> String.downcase
    Enum.member?(@extension_whitelist, file_extension)
  end

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

  # Override the persisted filenames:
  def filename(version, {file, _}) do
    "#{file.file_name}_#{version}"
  end

  # Override the storage directory:
  def storage_dir(version, {file, scope}) do
    "uploads/site_image/#{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

site_image.ex

defmodule Myapp.SiteImage do
  use Myapp.Web, :model
  use Arc.Ecto.Model

  schema "site_images" do
    field :image, Myapp.SiteImageUploader.Type
    belongs_to :site, Myapp.Site

    timestamps
  end

  @required_fields ~w(site_id)
  @optional_fields ~w()

  @required_file_fields ~w(image)
  @optional_file_fields ~w()

  @doc """
  Creates a changeset based on the `model` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> cast_attachments(params, @required_file_fields, @optional_file_fields)
  end

  def for_site(query, site_id) do
    from i in query,
    where: i.site_id == ^site_id
  end
end

site_images_controller.ex

  def create(conn, %{"site_image" => site_image_params}) do
    site_image_params = Map.put_new(site_image_params, "site_id", site_id(conn))
    changeset = SiteImage.changeset(%SiteImage{}, site_image_params)

    case Repo.insert(changeset) do
      {:ok, _site_image} ->
        conn
        |> put_flash(:info, "Successfully uploaded image.")
        |> redirect(to: site_images_path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

Uploading to S3 while model is being created

Apologies for using issues as a request for help, but hopefully someone can help me.

In the form, User model is created and avatar is uploaded at the same time. It means that Avatar is uploaded before User is created and has an id. I would like to give a photo UUID (rename from original filename my_photo.jpg to UUID.jpg) and persist that, instead of original file_name, like arc_ecto does by default.

What should I do to rename uploaded file to UUID and persist that to a database (instead of original filename)?

Is it possible to persist full path of uploaded file?

When to organize the upload folder structure, due to limitation count of subfolder, I have tried using UUID and date time, but it failed, seems filename() and storage_dir() are invoked in both store and get from database, so the expected resource url will be changed when query.

now I got an idea to use hash code of resource to organize the subfolder, but I am confused if this is the only way to do that, is there any other way such as persist full path in arc_ecto?

Scope id not available on insert with auto generated uuid

Hey,

So looking at the examples here is seem that everything is geared towards adding an image to an existing ecto model. I have an endpoint that uploads an image and some meta data and I would like to create the model and upload the image to S3 with the storage directory "uploads/#{scope.id}".

The problem is that at the time of insert the scope has no id, and I assumed that using server side generated uuids would fix this as the id is present before insertion. Im new to ecto/elixir/everything here so I dont understand the lifecycle that your working with, but is there some way to make the call to retrieve the storage_dir come after the auto generation of the uuid id?

Chris

Ecto model is not yet saved, when Arc is saving the file, resulting in empty scope

Using the local storage, i ran into a problem, where the folders would not be created upon model creation, my storage dir looks like this.

  # Override the storage directory:
  def storage_dir(_, {_, scope}) do
    module_name = scope.__info__(:module)
    |> Module.split
    |> List.last
    |> String.downcase
    "priv/static/images/#{module_name}/banner/#{scope.id}"
  end

As you can see i use the scope id, as suggested in the documentation; the problem is that Arc saves the file before the model i saved, which means that the id doesn't exist. This result in the files ending up in priv/static/images/#{module_name}/banner/ overwriting each other constantly.

This is what my saving proces looks like.

  def create(conn, %{"article" => article_params}) do
    # Add author.
    article_params = Dict.put_new(article_params, "admin_id", Plug.Conn.get_session(conn, :current_user))
    changeset = Article.changeset(%Article{}, article_params)

    case Repo.insert(changeset) do
      {:ok, _article} ->
        conn
        |> put_flash(:info, "Article created successfully.")
        |> redirect(to: admin_article_path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

Original filename is not available

When I save my file under a different name as the original file name, and I want to use the content_disposition header to serve the right file name, I can do that using s3_object_headers():

  def s3_object_headers(version, {file, scope}) do
    [
      content_disposition: "attachment; filename=#{scope.file}"
    ]
  end

However we have one problem. The filename is not yet available in the scope, and file also does not provide the original file name. I assume this is a bug (can we pass an up to date scope?), and otherwise how can we access the original filename?

Storing a file without Plug.Upload

Your example passes a Plug.Upload parameter to store the file in the database.
Can you provide an example of an update that just uploads a file from your hard drive?

I want to be able to do something like this:

    model_record = Repo.get!(MyTable, 1)

    {:ok, filename} = MyProject.Attachment.store({path, model_record})
    Repo.update!(%{model_record | attachment: filename})

The file is successfully stored on S3, but I am not sure what the changeset is expecting me to pass.

Absolute links for local urls

What do you think about having absolute links when generating urls for the local storage?

Today the urls are relatives, (without a forward slash at the start) this means that if I try to use it in a page that is not in the website root, it will not be found.

The change is very simple, just need to add a / at the start of the url.

I can't think in any problem it may cause, could also be a configuration if you don't want to change the current behaviour.

Uploads to S3 timeout

I'm not sure if this is related to arc_ecto, ex_aws or httpoison so I'm sorry if this is the wrong place, but any file over roughly 300KB seem to time out with the error below:

error] #PID<0.3140.0> running Agentport.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /dashboard/posts
** (exit) exited in: Task.await(%Task{pid: #PID<0.3143.0>, ref: #Reference<0.0.6.9316>}, 10000)
** (EXIT) time out

Small images upload without any issues though.

cast_attachments not passing scope to storage_dir when trying to override

I have an override for storage_dir in my uploader such as.

def storage_dir(version, {file, scope}) do
    "uploads/posts/photo/#{scope.id}"
end

I'v observed that scope is nil and fails at scope.id

I'v also inspected cast_attachments's assignment of scope and have seen that is does assign scope correctly in the context of the macro

I have also observed that destination_dir = definition.storage_dir(version, {file, scope}) passes scope as nil

It looks as if cast_attachment's arc_params remain empty

Is it posible to upload images from a remote url?

The Arc API docs show it being able to store an image from a web URL:

# Store any locally accessible file
Avatar.store("/path/to/my/file.png") #=> {:ok, "file.png"}

# Store any remotely accessible file
Avatar.store("http://example.com/image.png") #=> {:ok, "file.png"}

Is that posible with arc_ecto and if so could you give an example?

[image: {"is invalid", [type: AppName.ImageUploader.Type]}]

Back in February, I had arc_ecto configured and working just fine to upload images to S3 and save the path to my database. As sometimes happens with projects, the client didn't start using what I had built until earlier this week, and (without any changes to deps or related files since February on my part) things don't seem to work any longer. When I try to upload an image, I get the error mentioned in the title. Here's my ImageUploader file:

defmodule Ekf.ImageUploader do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original]

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

Here's my Ecto Model file:

defmodule Ekf.Image do
  use Ekf.Web, :model
  use Arc.Ecto.Schema

  schema "images" do
    belongs_to :static_page, Ekf.StaticPage
    belongs_to :class_page, Ekf.ClassPage
    belongs_to :instructor_page, Ekf.InstructorPage
    field :path, :string
    field :title, :string
    field :alt, :string
    field :label, :string
    field :image, Ekf.ImageUploader.Type

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:path, :title, :alt, :image, :label, :static_page_id, :class_page_id, :instructor_page_id])
    |> cast_attachments(params, [:image])
    |> validate_required([:title, :alt, :image, :label])
  end

  def path_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:path])
    |> validate_required([:path])
  end
end

Here's the map I'm passing to the changeset method on update, along with a model struct:

%{
label: "page-image",
image: %Plug.Upload{content_type: "image/jpeg", filename: "martial_arts_by_minorityman.jpg", path: "/var/folders/hw/cyd1v_z95l115dr3m5j08l5h0000gn/T//plug-1501/multipart-474368-69450-4"}, 
alt: "A powerful roundhouse kick.", 
title: "Muay Thai",
class_page_id: 2
}

Here's the error I'm getting in its entirety:

#Ecto.Changeset<action: nil, changes: %{},
 errors: [image: {"is invalid", [type: Ekf.ImageUploader.Type]}],
 data: #Ekf.Image<>, valid?: false>

I'm pretty stumped here. It seems as if the custom type has stopped working, and I could see that happening if any of deps had crept, but I really don't think they have. Here are my pinned versions for reference:

  defp deps do
    [{:phoenix, "~> 1.2.0"},
     {:phoenix_pubsub, "~> 1.0"},
     {:phoenix_ecto, "~> 3.0"},
     {:postgrex, ">= 0.0.0"},
     {:phoenix_html, "~> 2.6"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.11"},
     {:cowboy, "~> 1.0"},
     {:ex_machina, "~> 1.0"},
     {:comeonin, "~> 2.5"},
     {:guardian, "~> 0.13.0"},
     {:arc, "~> 0.5.2"},
     {:arc_ecto, "~> 0.4.4"},
     {:ex_aws, "~> 0.5.0"},
     {:poison, "~> 2.0"},
     {:httpoison, "~> 0.9.0"},
     {:bamboo, "~> 0.8.0"},
     {:bamboo_smtp, "~> 1.3.0"}
    ]
  end

Any help would be much appreciated.

`cast_attachments` uploads files even when passed invalid changeset

I'm using cast_attachments similar to the example provided with the README. I've experienced that the files are uploaded even though the normal cast that ran before this failed.

I'd expect the subsequent call to cast_attachments to not store files. In my specific case I was generating the filename from the scope data, which were invalid, so the filename was invalid too. Yet still the file was created on the S3 storage provided.

Is this the expected behaviour, and if yes then why so? :)

Create option, to specify base path of image urls

In a typical Phoenix app, you would probably like to store the uploaded the images in the priv/static directory, as it's exposed to the public.

As i result i specified the following in my uploader.

  # Override the storage directory:
  def storage_dir(version, {file, scope}) do
    "priv/static/images/company/logo/#{scope.id}"
  end

Now the problem is that, when i try to fetch a url for my model,

<img src="<%= Mobil.Logo.url({company.logo, company}, :thumb) %>

I following path returned

priv/static/images/company/logo/1/thumb.png?v=63608960064

Which is not wrong, but priv/static is exposed, as only what's static folder is, the ideal path would be.

/company/logo/1/thumb.png?v=63608960064

So i guess i need i way to specify, i don't know a base level? of which the url helpers should look in?

Image Uploading on S3 not working

Hello @stavro

I'm quite new to Phoenix & I liked your 2 libraries arc & arc_ecto, since they make it quite straightforward the whole process of uploading images on S3.

However for the past couple of days I couldn't make it to work & I don't know how to debug it, since I'm getting nothing useful in the stack-trace about possible issues.

I followed your implementation process & when I upload the image what I get as a response is that I have no image selected although the image is actually in it's temporary directory on the server.

This is a gist of the relevant code of my implementation of your library.

The versions of the libraries that I use are

  {:ex_aws, "~> 0.4.18"},
  {:httpoison, "~> 0.8.3"},
  {:arc_ecto, "~> 0.3.2"}

Any advice and insight is welcome,

Thanks!
@kexoth

Undefined function cast_attachments/4

Elixir 1.2.4, Erlang/OTP 18
Arc 0.5.2, Arc.Ecto 0.4.1
Phoenix 1.1.4, Phoenix.Ecto 3.0.0-rc.0, Ecto 2.0.0-rc

Piping the changeset to cast_attachments throws the following error:

== Compilation error on file web/models/album.ex ==
** (CompileError) web/models/album.ex:29: undefined function cast_attachments/4
    (stdlib) lists.erl:1337: :lists.foreach/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
defmodule BobbyHorton.Album do
  use BobbyHorton.Web, :model
  use Arc.Ecto.Schema
  alias BobbyHorton.PurchaseLink

  schema "albums" do
    field :title, :string
    field :release_date, Ecto.Date
    field :description, :string
    field :artwork, BobbyHorton.Artwork.Type

    timestamps
  end

  @required_fields ~w(title release_date description)
  @optional_fields ~w()
  @required_file_fields ~w()
  @optional_file_fields ~w(artwork)

  @doc """
  Creates a changeset based on the `model` and `params`.

  If no params are provided, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ %{}) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> cast_attachments(params, @required_file_fields, @optional_file_fields)
  end
end

no function clause matching in Arc.Ecto.Type.dump/2

Context:
This is what my attachment looks like:

%MyApp.Attachment{__meta__: #Ecto.Schema.Metadata<:built, "attachments">,
 file: %Plug.Upload{content_type: "image/png",
  filename: "Screen Shot 2016-09-07 at 3.28.23 PM.png",
  path: "/var/folders/6v/wlbdtzc11z52b2q5lxttmg4m0000gn/T//plug-1473/multipart-285821-783023-1"},
 id: nil, inserted_at: nil,
 submission: #Ecto.Association.NotLoaded<association :submission is not loaded>,
 submission_id: 16, updated_at: nil}

But when I try to do MyApp.Repo.insert attachment I get the following exception

Stacktrace:

** (FunctionClauseError) no function clause matching in Arc.Ecto.Type.dump/2
        (arc_ecto) lib/arc_ecto/type.ex:39: Arc.Ecto.Type.dump(MyApp.File, %Plug.Upload{content_type: "image/png", filename: "Screen Shot 2016-09-07 at 3.28.23 PM.png", path: "/var/folders/6v/wlbdtzc11z52b2q5lxttmg4m0000gn/T//plug-1473/multipart-285267-670891-1"})
        (ecto) lib/ecto/type.ex:629: Ecto.Type.do_adapter_dump/3
        (ecto) lib/ecto/repo/schema.ex:606: Ecto.Repo.Schema.dump_field!/6
        (ecto) lib/ecto/repo/schema.ex:619: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
        (stdlib) lists.erl:1262: :lists.foldl/3
        (ecto) lib/ecto/repo/schema.ex:617: Ecto.Repo.Schema.dump_fields!/5
        (ecto) lib/ecto/repo/schema.ex:566: Ecto.Repo.Schema.dump_changes!/6
        (ecto) lib/ecto/repo/schema.ex:182: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
        (ecto) lib/ecto/repo/schema.ex:595: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
        (ecto) lib/ecto/adapters/sql.ex:472: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
        (db_connection) lib/db_connection.ex:973: DBConnection.transaction_run/4
        (db_connection) lib/db_connection.ex:897: DBConnection.run_begin/3

This is my attachment model:

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

  schema "attachments" do
    field :file, MyApp.File.Type
    belongs_to :submission, MyApp.Submission

    timestamps()
  end

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

File is just:

defmodule MyApp.File do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original]
end

Is it something I'm missing? I looked at the line referenced in the stacktrace but it seemed like a red herring. Forgive me if I'm missing something obvious, new to elixir.

Arc ecto image fetch from aws s3

I am not sure whether this is a arc or arc_ecto or ex_aws issue, when I upload the image that works fine I guess, at least there are no errors. now while fetching the image like this MyApp.Avatar.url({ user.photo, user}, :thumb, signed: true)

it throws this error:

<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>
    The request signature we calculated does not match the signature you provided. Check your key and signing method.
  </Message>
  <AWSAccessKeyId>ACCESSID</AWSAccessKeyId>
  <StringToSign>
    AWS4-HMAC-SHA256 20160613T132235Z 20160613/us-east-1/s3/aws4_request 3119114
  </StringToSign>
  <SignatureProvided>
    f1e2764695
  </SignatureProvided>
  <StringToSignBytes>
     stuff
  </StringToSignBytes>
  <CanonicalRequest>
    GET /memento-is-dev/uploads/Screenshot%20from%202016-05-24%2014%3A43%3A13.png X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ACCESS%2F20160613%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20160613T131355Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&v=63633027268 host:s3.amazonaws.com host UNSIGNED-PAYLOAD
  </CanonicalRequest>
  <CanonicalRequestBytes>
    stuff
  </CanonicalRequestBytes>
  <HostId>
    fjO3c4Y=
  </HostId>
</Error>

I am using ecto 1.x with arc_ecto 0.3.2 and arc 0.5

here is some code in the controller to insert an image:

    case Repo.insert(changeset) do
      {:ok, prof} ->
        IO.inspect prof
        Expense.Avatar.store({transaction_params["image"], prof})

        conn
        |> put_flash(:info, "Profile created successfully")
        |> redirect(to: user_profile_path(conn, :index, user_id))
      {:error, changeset} ->
        render(conn, "index.html", changeset: changeset)
    end

Ex_machina integration

ex_machina is a de-facto standard for pre-building models in ExUnit. Unfortunately, I can't force it to work with arc_ecto. I'm not sure why:

defmodule MyReelty.Factory do
  use ExMachina.Ecto, repo: MyReelty.Repo

  def factory(:review) do
    %MyReelty.Review{
      city:            "Las Vegas",
      state:           "Nevada",
      country:         "USA",
      address:         "11th avenue",
      thumbnail_type:  "screenshot",
      zipcode:         1234,
      video:           %{file_name: "sample.mp4"}
    }
  end
end

And exception:

  1) test invalid parameters (MyReelty.Api.ReviewControllerTest)
     test/controllers/api/review_controller_test.exs:36
     ** (FunctionClauseError) no function clause matching in Arc.Ecto.Type.dump/2
     stacktrace:
       (arc_ecto) lib/arc_ecto/type.ex:19: Arc.Ecto.Type.dump(MyReelty.Video, %Plug.Upload{content_type: nil, filename: "sample.mp4", path: "test/fixtures/sample.mp4"})
       (ecto) lib/ecto/query/planner.ex:29: anonymous fn/6 in Ecto.Query.Planner.fields/4
       (stdlib) lists.erl:1262: :lists.foldl/3
       (ecto) lib/ecto/query/planner.ex:21: Ecto.Query.Planner.fields/4
       (ecto) lib/ecto/repo/schema.ex:449: Ecto.Repo.Schema.dump_changes/5
       (ecto) lib/ecto/repo/schema.ex:77: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
       (ecto) lib/ecto/repo/schema.ex:477: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/9
       (ecto) lib/ecto/pool.ex:292: Ecto.Pool.with_rollback/3
       (ecto) lib/ecto/adapters/sql.ex:582: Ecto.Adapters.SQL.transaction/8
       (ecto) lib/ecto/pool.ex:244: Ecto.Pool.outer_transaction/6
       (ecto) lib/ecto/adapters/sql.ex:551: Ecto.Adapters.SQL.transaction/3
       (ecto) lib/ecto/repo/schema.ex:14: Ecto.Repo.Schema.insert!/4
       (ex_machina) lib/ex_machina/ecto.ex:157: ExMachina.Ecto.save_record/3
       test/controllers/api/review_controller_test.exs:45: MyReelty.Api.ReviewControllerTest.__ex_unit_setup_1/1
       test/controllers/api/review_controller_test.exs:1: MyReelty.Api.ReviewControllerTest.__ex_unit__/

Is there any plans for further ex_machina integration? Thanks!

Can't use with Arc 0.6.0-rc1

Failed to use "arc" (version 0.6.0-rc1) because
  arc_ecto (versions 0.4.3 and 0.4.4) requires ~> 0.5.3
  Locked to 0.6.0-rc1 in your mix.lock

ProjectName.Avatar.Type causes Plug.conn bug?

I have arc and arc ecto up and running. I can upload files just fine.

However after a file is uploaded the controller always redirects to the initial page which the file was sent from.

I have tried to identify the source of the issue and it appears that the defining of a changeset causes the issue but no errors raised which makes it hard to determine what needs to be fixed.

e.g.

Model

schema "images" do
field :name, :string
field :avatar, BlogPhoenix.Avatar.Type
field :identifier, :string
end
....

Controller

image_params = {%Plug.Upload{....}

changeset = Image.changeset(%Image{}, Map.put(image_params, "identifier", random_number))

If I remove the image_params it can redirect the conn just fine.

e.g.
changeset = Image.changeset(%Image{}, %{random: "stuff")
^ This would cause a changeset issue but the conn can be manipulated normally.

Does anyone else experience this issue when calling the changeset?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.