Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow storage path to be different from url #11

Merged
merged 2 commits into from
Sep 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ erl_crash.dump
/waffletest
/doc
.env
/priv
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
3 changes: 2 additions & 1 deletion lib/waffle/definition/storage.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 32 additions & 11 deletions lib/waffle/storage/local.ex
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
defmodule Waffle.Storage.Local do
@moduledoc ~S"""
Local storage provides facility to store files locally.

## Local configuration

config :waffle,
storage: Waffle.Storage.Local,
# in order to have a different storage directory from url
starage_dir_prefix: "priv/waffle/private"

If you want to handle your attachements by phoenix application, configure the endpoint to serve it.

defmodule AppWeb.Endpoint do
plug Plug.Static,
at: "/uploads", from: Path.expand("./priv/waffle/public/uploads"), gzip: false
end
"""

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
Expand All @@ -26,14 +50,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
32 changes: 31 additions & 1 deletion test/storage/local_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand All @@ -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})
Expand All @@ -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)
Expand Down