Rsim

Rsim - is image manager that allows to upload and fetch images easily. It is integrated with ecto and AWS S3.


# will upload image to s3, save to DB and provide image_id
{:ok, image_id} = Rsim.save_image_from_file(path, :user)
# will upload image to s3 from provided URL, save to DB and provide image_id
{:ok, image_id} = Rsim.save_image_from_url(url, :user) 
 
 # returns image src
{:ok, image_src} = Rsim.get_image_url(image_id)
# resize image on the fly, saves new resized image and returns image src
{:ok, image_src} = Rsim.get_image_url(image_id, 200, 150)

Installation

Add rsim to your list of dependencies in mix.exs:

def deps do
  [
    {:rsim, "~> 0.1.0"}
  ]
end

# add configuration 
# currently only AWS S3 storage is suppoted so make sure
# you have config for :ex_aws as well  

config :ex_aws,
  access_key_id: "",
  secret_access_key: "",
  region: ""

config :rsim,
  repo: MyApp.Repo,
  s3: [bucket: "bucket-name"]

Copy and run migration to add images table from priv/repo/migrations/. Add image_id to your application tables that will have images.

  def change do
    alter table(:users) do
      add(:image_id, references(:images, on_delete: :nothing, type: :binary_id))
    end

    create(index(:users, [:image_id]))
  end
  
  
defmodule MyApp.Accounts.User do
  use Ecto.Schema
  schema "users" do
    # other fields
    belongs_to(:image, Rsim.EctoImage, type: :binary_id)
  end
end    

Real world example


defmodule MyApp.Accounts.Registrator do

  def register_user(user_params) do
    user_params = if !Map.has_key?(user_params, :image_id) && Map.has_key?(user_params, :image_url) do
      Map.put(user_params, :image_id, save_image_from_url!(user_params.image_url))

    sign_up_changeset(%User{}, user_params)
      |> Repo.insert()   
  end  

  defp save_image_from_url!(url) when is_nil(url), do: nil
  defp save_image_from_url!(url) do
    # case Rsim.save_image_from_file(uploaded_file.path, :user) do 
    case Rsim.save_image_from_url(url, :user) do
      {:ok, image} -> image.id
      {:error, error} -> nil
    end
  end
end


defmodule MyApp.Accounts.Avatar do
  alias MyApp.Accounts.User
  
  def fetch_avatar_url(user = %User{}) do
    fetch_image_url(user.image_id)
  end

  defp fetch_image_url(image_id) when is_nil(image_id), do: nil
  defp fetch_image_url(image_id) do
    # case Rsim.get_image_url(image_id) do          # get original image URL    
    case Rsim.get_image_url(image_id, 150, 150) do  #  resize image on the fly and get image_url
      {:ok, image_url} -> image_url
      {:error, _} -> nil
    end
  end
end

How It Works

__________
|  images |
|_________|
|    id   |    - unique image id
|   path  |    - path in storage 
|   mime  |    - image mime type
|   size  |    - image file size
|   width |    - image width
|  height |    - image height
|parent_id|    - reference for parent image (appears on resized images)
|_________|
     |
     |___________    
__________      |
|  users  |     |
|_________|     |
|    id   |     |
|   etc   |     |
| image_id| ----|
|         |
|_________|

When you upload new image via Rsim.save_image_from_file or Rsim.save_image_from_url it will be uploaded to storage. (Currently only AWS s3 is supported). Then image info will be saved to images table, including path in storage and some metadata like image width/height, mime, file size. images.id will be returned - it’s responsibility of your app to save it to application tables that have images.

When you fetch image URL - storage will transform path to valid image source. You also can get resized image on the fly. In that case new resized image will be saves with reference images.parent_id to original image.