arc icon indicating copy to clipboard operation
arc copied to clipboard

Generate random file name

Open Linuus opened this issue 9 years ago • 5 comments

Hi!

My model can have multiple images so I need the filenames to be unique, is there any way to do this? I have tried to do it in my changeset function but I can't make it work since cast_attachment uploads the image directly.

Any ideas?

Linuus avatar Jun 28 '16 13:06 Linuus

What I ended up doing was this:

  def changeset(struct, params \\ %{}) do
    params = put_random_filename(params)

    struct
    |> cast_attachments(params, [:image])
    |> validate_required([:image])
  end

  defp put_random_filename(%{"image" => %Plug.Upload{filename: name} = image} = params) do
    image = %Plug.Upload{image | filename: random_filename(name)}
    %{params | "image" => image}
  end
  defp put_random_filename(params), do: params

  defp random_filename(name) do
    (:crypto.strong_rand_bytes(20) |> Base.url_encode64 |> binary_part(0, 20)) <> name
  end

... which doesn't seem very pretty :)

Any tips suggestions on how to make it better is much appreciated.

Linuus avatar Jun 28 '16 14:06 Linuus

I did something similar, and like you I also felt as though changeset functions were the only logical place for this.

I had a good number of models with this requirement, so I made a helper module with one publicly accessible function, prepend_filenames(params, [field | rest]), and then called it where you're calling it. The only difference is that I limited myself to prepending a unix timestamp, like DateTime.utc_now |> DateTime.to_unix |> Integer.to_string

I also had to add a bit of support for detecting when a file should be deleted due to an update, which also needed to be used from the changeset function:

  def delete_overwritten_files!( kind, entity, changeset ) do
    changeset.changes
    |> Enum.filter( fn({k,_v}) -> k in kind.arc_fields end )
    |> Enum.each( fn({k,_v}) ->
      arc_def = get_arc_definition(kind, k)
      delete_one_attachment( arc_def, entity, Map.get(entity,k) )
    end)
    changeset
  end

mrluc avatar Aug 02 '16 20:08 mrluc

I'd be awesome to see this supported by Arc someday. I just attempted this since I wasn't going to have an ID to use and it took awhile to figure out it wouldn't work.

jakecraige avatar Nov 20 '16 03:11 jakecraige

Thx for the code @Linuus!

My first attempt at solving this problem was modifying the filename method in my arc definition file. As I soon discovered, filename is called not just on insertion of new files but on retrieval as well, making it a poor fit for a randomizer.

Further, relying on existing schema properties within the filename method works well for existing models, but breaks when working with new, dataless models.

Additionally, editing the filename method disrupts all prior file upload paths.

The code above seems to be the best pattern for preventing file collisions on both new and existing models from my (limited) research.

makeitrein avatar Aug 13 '18 04:08 makeitrein

Hey, I just need to implement something to delete overwritten files, something like @mrluc said. But I can't figure out how to achieve that goal, @mrluc could you put a more detailed example, please?

Thank you!

Yamilquery avatar Apr 08 '19 19:04 Yamilquery