From 3c317e6075a2ae01465948d20307a43b2a051bfd Mon Sep 17 00:00:00 2001 From: Boris Kuznetsov Date: Wed, 4 Sep 2019 21:15:44 +0300 Subject: [PATCH] allow storage path to be different from url --- .gitignore | 1 + README.md | 10 ++++++++++ lib/waffle/definition/storage.ex | 3 ++- lib/waffle/storage/local.ex | 26 +++++++++++++++----------- test/storage/local_test.exs | 32 +++++++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 7663de6..f48eda9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ erl_crash.dump /waffletest /doc .env +/priv diff --git a/README.md b/README.md index fc8a82b..66dff58 100644 --- a/README.md +++ b/README.md @@ -275,12 +275,22 @@ Waffle currently supports Amazon S3 and local destinations for file uploads. ### Local Configuration +```elixir +config :waffle, + storage: Waffle.Storage.Local, + # in order to have a different storage directory from url + starage_dir_prefix: "priv/waffle/private" +``` + To store your attachments locally, override the `__storage` function in your definition module to `Waffle.Storage.Local`. You may wish to optionally override the storage directory as well, as outlined below. + ```elixir defmodule Avatar do use Waffle.Definition def __storage, do: Waffle.Storage.Local # Add this + # in order to have a different storage directory from url + def starage_dir_prefix(), do: "priv/waffle/private" end ``` diff --git a/lib/waffle/definition/storage.ex b/lib/waffle/definition/storage.ex index 4889992..4422905 100644 --- a/lib/waffle/definition/storage.ex +++ b/lib/waffle/definition/storage.ex @@ -7,13 +7,14 @@ defmodule Waffle.Definition.Storage do def bucket, do: Application.fetch_env!(:waffle, :bucket) def asset_host, do: Application.get_env(:waffle, :asset_host) def filename(_, {file, _}), do: Path.basename(file.file_name, Path.extname(file.file_name)) + def storage_dir_prefix(), do: Application.get_env(:waffle, :storage_dir_prefix, "") def storage_dir(_, _), do: Application.get_env(:waffle, :storage_dir, "uploads") def validate(_), do: true def default_url(version, _), do: default_url(version) def default_url(_), do: nil def __storage, do: Application.get_env(:waffle, :storage, Waffle.Storage.S3) - defoverridable [storage_dir: 2, filename: 2, validate: 1, default_url: 1, default_url: 2, __storage: 0, bucket: 0, asset_host: 0] + defoverridable [storage_dir_prefix: 0, storage_dir: 2, filename: 2, validate: 1, default_url: 1, default_url: 2, __storage: 0, bucket: 0, asset_host: 0] @before_compile Waffle.Definition.Storage end diff --git a/lib/waffle/storage/local.ex b/lib/waffle/storage/local.ex index 62f5821..107a514 100644 --- a/lib/waffle/storage/local.ex +++ b/lib/waffle/storage/local.ex @@ -1,20 +1,27 @@ defmodule Waffle.Storage.Local do + def put(definition, version, {file, scope}) do - destination_dir = definition.storage_dir(version, {file, scope}) - path = Path.join(destination_dir, file.file_name) - path |> Path.dirname() |> File.mkdir_p!() + destination_path = Path.join([ + definition.storage_dir_prefix(), + definition.storage_dir(version, {file, scope}), + file.file_name + ]) + destination_path |> Path.dirname() |> File.mkdir_p!() if binary = file.binary do - File.write!(path, binary) + File.write!(destination_path, binary) else - File.copy!(file.path, path) + File.copy!(file.path, destination_path) end {:ok, file.file_name} end def url(definition, version, file_and_scope, _options \\ []) do - local_path = build_local_path(definition, version, file_and_scope) + local_path = Path.join([ + definition.storage_dir(version, file_and_scope), + Waffle.Definition.Versioning.resolve_file_name(definition, version, file_and_scope) + ]) url = if String.starts_with?(local_path, "/") do local_path @@ -26,14 +33,11 @@ defmodule Waffle.Storage.Local do end def delete(definition, version, file_and_scope) do - build_local_path(definition, version, file_and_scope) - |> File.rm() - end - - defp build_local_path(definition, version, file_and_scope) do Path.join([ + definition.storage_dir_prefix(), definition.storage_dir(version, file_and_scope), Waffle.Definition.Versioning.resolve_file_name(definition, version, file_and_scope) ]) + |> File.rm() end end diff --git a/test/storage/local_test.exs b/test/storage/local_test.exs index 9506587..c346a94 100644 --- a/test/storage/local_test.exs +++ b/test/storage/local_test.exs @@ -18,7 +18,6 @@ defmodule WaffleTest.Storage.Local do defmodule DummyDefinition do use Waffle.Definition - @acl :public_read @versions [:original, :thumb, :skipped] def transform(:thumb, _), do: {:convert, "-strip -thumbnail 10x10"} @@ -33,6 +32,21 @@ defmodule WaffleTest.Storage.Local do def filename(:skipped, {file, _}), do: "1/skipped-#{Path.basename(file.file_name, Path.extname(file.file_name))}" end + defmodule DummyDefinitionWithPrefix do + use Waffle.Definition + + @versions [:original, :thumb] + + def transform(:thumb, _), do: {:convert, "-strip -thumbnail 10x10"} + + def storage_dir_prefix(), do: "priv/waffle/private" + def storage_dir(_, _), do: "waffletest/uploads" + def __storage, do: Waffle.Storage.Local + + def filename(:original, {file, _}), do: "original-#{Path.basename(file.file_name, Path.extname(file.file_name))}" + def filename(:thumb, {file, _}), do: "1/thumb-#{Path.basename(file.file_name, Path.extname(file.file_name))}" + end + test "put, delete, get" do assert {:ok, "original-image.png"} == Waffle.Storage.Local.put(DummyDefinition, :original, {Waffle.File.new(%{filename: "original-image.png", path: @img}), nil}) assert {:ok, "1/thumb-image.png"} == Waffle.Storage.Local.put(DummyDefinition, :thumb, {Waffle.File.new(%{filename: "1/thumb-image.png", path: @img}), nil}) @@ -48,6 +62,22 @@ defmodule WaffleTest.Storage.Local do refute File.exists?("waffletest/uploads/1/thumb-image.png") end + test "put, delete, get with storage prefix" do + assert {:ok, "original-image.png"} == Waffle.Storage.Local.put(DummyDefinitionWithPrefix, :original, {Waffle.File.new(%{filename: "original-image.png", path: @img}), nil}) + assert {:ok, "1/thumb-image.png"} == Waffle.Storage.Local.put(DummyDefinitionWithPrefix, :thumb, {Waffle.File.new(%{filename: "1/thumb-image.png", path: @img}), nil}) + + assert File.exists?("priv/waffle/private/waffletest/uploads/original-image.png") + assert File.exists?("priv/waffle/private/waffletest/uploads/1/thumb-image.png") + assert "/waffletest/uploads/original-image.png" == DummyDefinitionWithPrefix.url("image.png", :original) + assert "/waffletest/uploads/1/thumb-image.png" == DummyDefinitionWithPrefix.url("1/image.png", :thumb) + + :ok = Waffle.Storage.Local.delete(DummyDefinitionWithPrefix, :original, {%{file_name: "image.png"}, nil}) + :ok = Waffle.Storage.Local.delete(DummyDefinitionWithPrefix, :thumb, {%{file_name: "image.png"}, nil}) + refute File.exists?("priv/waffle/private/waffletest/uploads/original-image.png") + refute File.exists?("priv/waffle/private/waffletest/uploads/1/thumb-image.png") + end + + test "deleting when there's a skipped version" do DummyDefinition.store(@img) assert :ok = DummyDefinition.delete(@img)