Skip to content

Commit 0bb4cda

Browse files
committed
Add opts to upload
I've found the need to pass along the content type and disposition to Amazon in order to have the correct metadata set on the object. I did not want to load the entire binary into memory to call `write`. Instead, I would like to stream the file from disk.
1 parent 3c80e73 commit 0bb4cda

File tree

9 files changed

+69
-35
lines changed

9 files changed

+69
-35
lines changed

lib/file_store.ex

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,34 @@ defprotocol FileStore do
1616
it's usage.
1717
"""
1818

19-
@type key :: binary()
20-
@type list_opts :: [{:prefix, binary()}]
21-
@type delete_all_opts :: [{:prefix, binary()}]
19+
@type key :: String.t()
20+
21+
@type prefix_opt :: {:prefix, String.t()}
22+
@type content_type_opt :: {:content_type, String.t()}
23+
@type disposition_opt :: {:disposition, String.t()}
24+
@type expires_in_opt :: {:expires_in, pos_integer()}
25+
26+
@type list_opts :: [prefix_opt()]
27+
@type delete_all_opts :: [prefix_opt()]
2228
@type write_opts :: [
23-
{:content_type, binary()}
24-
| {:disposition, binary()}
29+
content_type_opt()
30+
| disposition_opt()
31+
]
32+
33+
@type upload_opts :: [
34+
content_type_opt()
35+
| disposition_opt()
2536
]
2637

2738
@type public_url_opts :: [
28-
{:content_type, binary()}
29-
| {:disposition, binary()}
39+
content_type_opt()
40+
| disposition_opt()
3041
]
3142

3243
@type signed_url_opts :: [
33-
{:content_type, binary()}
34-
| {:disposition, binary()}
35-
| {:expires_in, integer()}
44+
content_type_opt()
45+
| disposition_opt()
46+
| expires_in_opt()
3647
]
3748

3849
@doc """
@@ -50,7 +61,12 @@ defprotocol FileStore do
5061
:ok
5162
5263
"""
53-
@spec write(t, key, binary, write_opts) :: :ok | {:error, term}
64+
@spec write(
65+
store :: t(),
66+
key :: key(),
67+
content :: binary(),
68+
opts :: write_opts()
69+
) :: :ok | {:error, term()}
5470
def write(store, key, content, opts \\ [])
5571

5672
@doc """
@@ -62,7 +78,7 @@ defprotocol FileStore do
6278
{:ok, "hello world"}
6379
6480
"""
65-
@spec read(t, key) :: {:ok, binary} | {:error, term}
81+
@spec read(store :: t(), key :: key()) :: {:ok, binary()} | {:error, term()}
6682
def read(store, key)
6783

6884
@doc """
@@ -75,8 +91,13 @@ defprotocol FileStore do
7591
:ok
7692
7793
"""
78-
@spec upload(t, Path.t(), key) :: :ok | {:error, term}
79-
def upload(store, source, key)
94+
@spec upload(
95+
store :: t(),
96+
source :: Path.t(),
97+
key :: key(),
98+
opts :: upload_opts()
99+
) :: :ok | {:error, term()}
100+
def upload(store, source, key, opts \\ [])
80101

81102
@doc """
82103
Download a file from the store and save it to the given `path`.
@@ -87,7 +108,11 @@ defprotocol FileStore do
87108
:ok
88109
89110
"""
90-
@spec download(t, key, Path.t()) :: :ok | {:error, term}
111+
@spec download(
112+
store :: t(),
113+
key :: key(),
114+
destination :: Path.t()
115+
) :: :ok | {:error, term()}
91116
def download(store, key, destination)
92117

93118
@doc """
@@ -99,7 +124,7 @@ defprotocol FileStore do
99124
{:ok, %FileStore.Stat{key: "foo", etag: "2e5pd429", size: 24}}
100125
101126
"""
102-
@spec stat(t, key) :: {:ok, FileStore.Stat.t()} | {:error, term}
127+
@spec stat(store :: t(), key :: key()) :: {:ok, FileStore.Stat.t()} | {:error, term()}
103128
def stat(store, key)
104129

105130
@doc """
@@ -111,7 +136,7 @@ defprotocol FileStore do
111136
:ok
112137
113138
"""
114-
@spec delete(t, key) :: :ok | {:error, term}
139+
@spec delete(store :: t(), key :: key()) :: :ok | {:error, term()}
115140
def delete(store, key)
116141

117142
@doc """
@@ -130,7 +155,7 @@ defprotocol FileStore do
130155
:ok
131156
132157
"""
133-
@spec delete_all(t, delete_all_opts) :: :ok | {:error, term}
158+
@spec delete_all(store :: t(), opts :: delete_all_opts()) :: :ok | {:error, term()}
134159
def delete_all(store, opts \\ [])
135160

136161
@doc """
@@ -142,7 +167,7 @@ defprotocol FileStore do
142167
:ok
143168
144169
"""
145-
@spec copy(t(), key(), key()) :: :ok | {:error, term()}
170+
@spec copy(store :: t(), src :: key(), dest :: key()) :: :ok | {:error, term()}
146171
def copy(store, src, dest)
147172

148173
@doc """
@@ -156,7 +181,7 @@ defprotocol FileStore do
156181
:ok
157182
158183
"""
159-
@spec rename(t(), key(), key()) :: :ok | {:error, term()}
184+
@spec rename(strore :: t(), src :: key(), dest :: key()) :: :ok | {:error, term()}
160185
def rename(store, src, dest)
161186

162187
@doc """
@@ -173,7 +198,7 @@ defprotocol FileStore do
173198
"https://mybucket.s3-us-east-1.amazonaws.com/foo"
174199
175200
"""
176-
@spec get_public_url(t, key, public_url_opts) :: binary
201+
@spec get_public_url(strore :: t(), key :: key(), opts :: public_url_opts()) :: String.t()
177202
def get_public_url(store, key, opts \\ [])
178203

179204
@doc """
@@ -192,7 +217,11 @@ defprotocol FileStore do
192217
{:ok, "https://s3.amazonaws.com/mybucket/foo?X-AMZ-Expires=3600&..."}
193218
194219
"""
195-
@spec get_signed_url(t, key, signed_url_opts) :: {:ok, binary} | {:error, term}
220+
@spec get_signed_url(
221+
store :: t(),
222+
key :: key(),
223+
opts :: signed_url_opts()
224+
) :: {:ok, binary()} | {:error, term()}
196225
def get_signed_url(store, key, opts \\ [])
197226

198227
@doc """
@@ -211,6 +240,6 @@ defprotocol FileStore do
211240
["foo/bar"]
212241
213242
"""
214-
@spec list!(t, list_opts) :: Enumerable.t()
243+
@spec list!(store :: t(), opts :: list_opts()) :: Enumerable.t()
215244
def list!(store, opts \\ [])
216245
end

lib/file_store/adapters/disk.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ defmodule FileStore.Adapters.Disk do
127127
do: File.rename(src, dest)
128128
end
129129

130-
def upload(store, source, key) do
130+
def upload(store, source, key, _opts \\ []) do
131131
with {:ok, dest} <- expand(store, key),
132132
{:ok, _} <- File.copy(source, dest),
133133
do: :ok

lib/file_store/adapters/memory.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ defmodule FileStore.Adapters.Memory do
154154
end)
155155
end
156156

157-
def upload(store, source, key) do
157+
def upload(store, source, key, _opts \\ []) do
158158
with {:ok, data} <- File.read(source) do
159159
write(store, key, data)
160160
end

lib/file_store/adapters/null.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ defmodule FileStore.Adapters.Null do
4343

4444
def delete(_store, _key), do: :ok
4545
def delete_all(_store, _opts), do: :ok
46-
def upload(_store, _source, _key), do: :ok
46+
def upload(_store, _source, _key, _opts \\ []), do: :ok
4747
def download(_store, _key, _destination), do: :ok
4848
def write(_store, _key, _content, _opts \\ []), do: :ok
4949
def read(_store, _key), do: {:ok, ""}

lib/file_store/adapters/s3.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,15 @@ if Code.ensure_loaded?(ExAws.S3) do
133133
end
134134
end
135135

136-
def upload(store, source, key) do
136+
def upload(store, source, key, opts \\ []) do
137+
opts =
138+
opts
139+
|> Keyword.take([:content_type, :disposition])
140+
|> Utils.rename_key(:disposition, :content_disposition)
141+
137142
source
138143
|> ExAws.S3.Upload.stream_file()
139-
|> ExAws.S3.upload(store.bucket, key)
144+
|> ExAws.S3.upload(store.bucket, key, opts)
140145
|> acknowledge(store)
141146
rescue
142147
error in [File.Error] -> {:error, error.reason}

lib/file_store/middleware/errors.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ defmodule FileStore.Middleware.Errors do
7070
|> wrap(RenameError, src: src, dest: dest)
7171
end
7272

73-
def upload(store, path, key) do
73+
def upload(store, path, key, opts) do
7474
store.__next__
75-
|> FileStore.upload(path, key)
75+
|> FileStore.upload(path, key, opts)
7676
|> wrap(UploadError, path: path, key: key)
7777
end
7878

lib/file_store/middleware/logger.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ defmodule FileStore.Middleware.Logger do
4545
|> log("RENAME", src: src, dest: dest)
4646
end
4747

48-
def upload(store, source, key) do
48+
def upload(store, source, key, opts) do
4949
store.__next__
50-
|> FileStore.upload(source, key)
50+
|> FileStore.upload(source, key, opts)
5151
|> log("UPLOAD", key: key)
5252
end
5353

lib/file_store/middleware/prefix.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ defmodule FileStore.Middleware.Prefix do
5454
FileStore.rename(store.__next__, put_prefix(src, store), put_prefix(dest, store))
5555
end
5656

57-
def upload(store, source, key) do
58-
FileStore.upload(store.__next__, source, put_prefix(key, store))
57+
def upload(store, source, key, opts) do
58+
FileStore.upload(store.__next__, source, put_prefix(key, store), opts)
5959
end
6060

6161
def download(store, key, dest) do

test/support/error_adapter.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule FileStore.Adapters.Error do
1010
defimpl FileStore do
1111
def write(_store, _key, _content, _opts \\ []), do: {:error, :boom}
1212
def read(_store, _key), do: {:error, :boom}
13-
def upload(_store, _source, _key), do: {:error, :boom}
13+
def upload(_store, _source, _key, _opts \\ []), do: {:error, :boom}
1414
def download(_store, _key, _destination), do: {:error, :boom}
1515
def stat(_store, _key), do: {:error, :boom}
1616
def delete(_store, _key), do: {:error, :boom}

0 commit comments

Comments
 (0)