stavro / arc_ecto

An integration with Arc and Ecto.
255 stars 149 forks source link

Adding or changing versions #122

Open dipth opened 3 years ago

dipth commented 3 years ago

What is the best way to regenerate a version for an attachment when having added a new version definition or changed an existing one.

For instance, let's say I have the following version definitions:

defmodule MyAppWeb.PhotoFile do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original, :large, :thumb]
  @extension_whitelist ~w(.jpg .jpeg .gif .png)

  def acl(:large, _), do: :public_read
  def acl(:thumb, _), do: :public_read

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

  def transform(:large, _) do
    {:convert, "-define jpeg:size=2400x2400 -auto-orient -strip -resize 1200x1200>"}
  end

  def transform(:thumb, _) do
    {:convert, "-define jpeg:size=500x500 -auto-orient -strip -thumbnail 250x250^ -gravity center -extent 250x250"}
  end

  def filename(version, _) do
    version
  end

  def storage_dir(_version, {_file, photo}) do
    "uploads/photos/#{photo.uuid}"
  end
end

Supposed I've been having this running in production for a while and I now find out that I also need a tiny_thumb version, I would change the definitions to add:

defmodule MyAppWeb.PhotoFile do
  use Arc.Definition
  use Arc.Ecto.Definition

  @versions [:original, :large, :thumb, :tiny_thumb]
  @extension_whitelist ~w(.jpg .jpeg .gif .png)

  def acl(:large, _), do: :public_read
  def acl(:thumb, _), do: :public_read
  def acl(:tiny_thumb, _), do: :public_read

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

  def transform(:large, _) do
    {:convert, "-define jpeg:size=2400x2400 -auto-orient -strip -resize 1200x1200>"}
  end

  def transform(:thumb, _) do
    {:convert, "-define jpeg:size=500x500 -auto-orient -strip -thumbnail 250x250^ -gravity center -extent 250x250"}
  end

  def transform(:tiny_thumb, _) do
    {:convert, "-define jpeg:size=500x500 -auto-orient -strip -thumbnail 32x32^ -gravity center -extent 32x32"}
  end

  def filename(version, _) do
    version
  end

  def storage_dir(_version, {_file, photo}) do
    "uploads/photos/#{photo.uuid}"
  end
end

This will work fine for new photos uploaded after the change has been deployed, but existing photos will now fail when requesting MyAppWeb.PhotoFile.url({@photo.file, @photo}, :tiny_thumb). So what would be the best way to traverse through all existing photos and create the missing version?

achempion commented 3 years ago

Hi,

You just create a function and call it to process all files before deploying a new feature which uses these file sizes.

dipth commented 3 years ago

@achempion sure, but is there a function that can be called to generate this version or do I manually have to write code that downloads the original images from S3 and re-attaches them to regenerate all versions?

achempion commented 3 years ago

I looks like you can't process only one version for now and have to trigger cropping for all versions at once upon adding a new version.