Changing waffle filename implementation on the fly

Hi, I’m using waffle to upload files a s3 bucket. It’s been working well by I’ve decided I wan’t to change what names are used for the files. So i implement the filename/2 as follows:

 # Override the persisted filenames: def filename(version, {file, scope}) do file_name = Path.basename(file.file_name, Path.extname(file.file_name)) "#{file_name}_#{version}_#{:os.system_time()}" end 

But now all my already uploaded files have incorrect references to them. Anyone know of a good way to do this, I guess I could just compare the files updated_at timestamp to see if it happend before or after the updated filename definition but I get the feeling I’m missing a more elegant solution.

Thanks in advance, any suggestions are appreciated :slight_smile:

Not sure there’s an elegant solution per se. Maybe you should make a script that retroactively changes the filenames of all files who don’t conform to the new format yet.

You might be interested in this issue on Github: Support dynamic file naming.

Had the same problem recently, you can also make the change of filename by tweaking the changeset (see https://github.com/elixir-waffle/waffle_ecto/issues/13) However, Waffle automatically added file extension and I gave up with this solution. Using the underlying ExAws.S3 is actually not hard and gives more flexibility if you don’t need advanced Waffle features.

2 Likes

Thanks for your suggestions!
After banging my head against this trying to to find a safe and effective solution i decided to solve the problem outside of waffle. So in my controllers I now have a function that goes through the attachments in the parameters and adjusts the filenames in the different %Plug.Upload{}.

 def unique_attachments(params, options \\ []) do defaults = [ field: "attachments" ] options = defaults |> Keyword.merge(options) |> Enum.into(%{}) attachments = Map.get(params, options.field, %{}) Enum.reduce(attachments, %{}, fn {key, value}, acc -> case Map.get(value, "file", nil) do nil -> Map.put(acc, key, value) file -> filename = Map.fetch!(file, :filename) unique_filename = "#{:os.system_time()}_#{filename}" file = Map.put(file, :filename, unique_filename) value = Map.put(value, "file", file) Map.put(acc, key, value) end end) end 

It’s not elegant but it works and it does not risk corrupting my old existing attachments, since it will only apply to new files. :slightly_smiling_face:

1 Like