Skip to content

Commit 735c731

Browse files
authored
Merge pull request #131 from fastjames/fastjames/feature/stream_upload
Accept Streams as attachments
2 parents 8b058e5 + 1c53d5b commit 735c731

File tree

4 files changed

+59
-9
lines changed

4 files changed

+59
-9
lines changed

lib/waffle/actions/store.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ defmodule Waffle.Actions.Store do
1212
* A map with a filename and path keys (eg, a `%Plug.Upload{}`)
1313
1414
* A map with a filename and binary keys (eg, `%{filename: "image.png", binary: <<255,255,255,...>>}`)
15+
* A map with a filename and stream keys (eg, `%{filename: "image.png", stream: %Stream{...}}`)
1516
1617
* A two-element tuple consisting of one of the above file formats as well as a scope map
1718
@@ -50,11 +51,13 @@ defmodule Waffle.Actions.Store do
5051
end
5152
end
5253

53-
def store(definition, {file, scope}) when is_binary(file) or is_map(file) do
54+
def store(definition, {file, scope})
55+
when is_binary(file) or is_map(file) do
5456
put(definition, {Waffle.File.new(file, definition), scope})
5557
end
5658

57-
def store(definition, filepath) when is_binary(filepath) or is_map(filepath) do
59+
def store(definition, filepath)
60+
when is_binary(filepath) or is_map(filepath) do
5861
store(definition, {filepath, nil})
5962
end
6063

lib/waffle/file.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Waffle.File do
22
@moduledoc false
33

4-
defstruct [:path, :file_name, :binary, :is_tempfile?]
4+
defstruct [:path, :file_name, :binary, :is_tempfile?, :stream]
55

66
def generate_temporary_path(item \\ nil) do
77
do_generate_temporary_path(item)
@@ -92,6 +92,13 @@ defmodule Waffle.File do
9292
end
9393
end
9494

95+
#
96+
# Handle a stream
97+
#
98+
def new(%{filename: filename, stream: stream}, _definition) when is_struct(stream) do
99+
%Waffle.File{stream: stream, file_name: Path.basename(filename)}
100+
end
101+
95102
#
96103
# Support functions
97104
#

lib/waffle/storage/s3.ex

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,22 +192,35 @@ defmodule Waffle.Storage.S3 do
192192
end
193193
end
194194

195+
# If the file is a stream, send it to AWS as a multi-part upload
196+
defp do_put(file = %Waffle.File{stream: file_stream}, {s3_bucket, s3_key, s3_options})
197+
when is_struct(file_stream) do
198+
file_stream
199+
|> chunk_stream()
200+
|> do_put_stream(file, {s3_bucket, s3_key, s3_options})
201+
end
202+
195203
# Stream the file and upload to AWS as a multi-part upload
196204
defp do_put(file, {s3_bucket, s3_key, s3_options}) do
197205
file.path
198206
|> Upload.stream_file()
207+
|> do_put_stream(file, {s3_bucket, s3_key, s3_options})
208+
end
209+
210+
defp do_put_stream(stream, file, {s3_bucket, s3_key, s3_options}) do
211+
stream
199212
|> S3.upload(s3_bucket, s3_key, s3_options)
200213
|> ExAws.request()
201214
|> case do
202-
{:ok, %{status_code: 200}} -> {:ok, file.file_name}
203-
{:ok, :done} -> {:ok, file.file_name}
204-
{:error, error} -> {:error, error}
205-
end
215+
{:ok, %{status_code: 200}} -> {:ok, file.file_name}
216+
{:ok, :done} -> {:ok, file.file_name}
217+
{:error, error} -> {:error, error}
218+
end
206219
rescue
207220
e in ExAws.Error ->
208221
Logger.error(inspect(e))
209-
Logger.error(e.message)
210-
{:error, :invalid_bucket}
222+
Logger.error(e.message)
223+
{:error, :invalid_bucket}
211224
end
212225

213226
defp build_url(definition, version, file_and_scope, _options) do
@@ -264,4 +277,22 @@ defmodule Waffle.Storage.S3 do
264277

265278
defp parse_bucket({:system, env_var}) when is_binary(env_var), do: System.get_env(env_var)
266279
defp parse_bucket(name), do: name
280+
281+
defp chunk_stream(stream, chunk_size \\ 5 * 1024 * 1024) do
282+
Stream.chunk_while(
283+
stream,
284+
"",
285+
fn element, acc ->
286+
if String.length(acc) >= chunk_size do
287+
{:cont, acc, element}
288+
else
289+
{:cont, acc <> element}
290+
end
291+
end,
292+
fn
293+
[] -> {:cont, []}
294+
acc -> {:cont, acc, []}
295+
end
296+
)
297+
end
267298
end

test/storage/s3_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,15 @@ defmodule WaffleTest.Storage.S3 do
218218
delete_and_assert_not_found(DummyDefinition, "image.png")
219219
end
220220

221+
@tag :s3
222+
@tag timeout: 15_000
223+
test "public put stream" do
224+
img_map = %{filename: "image.png", stream: File.stream!(@img)}
225+
assert {:ok, "image.png"} == DummyDefinition.store(img_map)
226+
assert_public(DummyDefinition, "image.png")
227+
delete_and_assert_not_found(DummyDefinition, "image.png")
228+
end
229+
221230
@tag :s3
222231
@tag timeout: 15_000
223232
test "private put and signed get" do

0 commit comments

Comments
 (0)