From 8f2f221b4fc4d82f5c2d633d228b1b1cfffc972c Mon Sep 17 00:00:00 2001 From: yunmikun Date: Wed, 17 Jun 2020 13:32:47 +0300 Subject: [PATCH] Add base64-encoded images support When casting an image in base64-encoded format it will be casted to a binary image with generated file name. --- lib/waffle_ecto/schema.ex | 17 +++++++++++++++++ test/schema_test.exs | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/waffle_ecto/schema.ex b/lib/waffle_ecto/schema.ex index 5c7f7d0..c7077ce 100644 --- a/lib/waffle_ecto/schema.ex +++ b/lib/waffle_ecto/schema.ex @@ -104,6 +104,18 @@ defmodule Waffle.Ecto.Schema do when is_binary(filename) and is_binary(binary) -> [{field, {upload, scope}} | fields] + {field, data = "data:image/" <> _}, fields -> + encoded_image = String.replace(data, ~r/^data\:image\/.*;base64,/, "") + + case Base.decode64(encoded_image) do + {:ok, binary_image} -> + upload = %{filename: unique_file_name(data), binary: binary_image} + [{field, {upload, scope}} | fields] + + _ -> + fields + end + # If casting a binary (path), ensure we've explicitly allowed paths {field, path}, fields when is_binary(path) -> path = String.trim(path) @@ -124,6 +136,11 @@ defmodule Waffle.Ecto.Schema do end) end + defp unique_file_name("data:image/" <> rest) do + extension = String.replace(rest, ~r/;base64,.*/, "") + "#{Ecto.UUID.generate()}.#{extension}" + end + def convert_params_to_binary(params) do Enum.reduce(params, nil, fn {key, _value}, nil when is_binary(key) -> diff --git a/test/schema_test.exs b/test/schema_test.exs index 8211036..03bb818 100644 --- a/test/schema_test.exs +++ b/test/schema_test.exs @@ -175,4 +175,16 @@ defmodule WaffleTest.Ecto.Schema do ) ) end + + test_with_mock "casting base64-encoded data struct attachments", DummyDefinition, + store: fn {%{filename: _, binary: _}, %TestUser{}} -> + {:ok, "file.png"} + end do + cs = TestUser.changeset(%TestUser{}, %{ + "avatar" => "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" + }) + + assert cs.valid? + assert %{avatar: %{file_name: "file.png", updated_at: _}} = cs.changes + end end