Kurisu

Kurisu

How can I use unique filename generator function with arc_ecto?

I’ve already created my own function to generate unique string name per given directory, using a counter stored in the directory.

MyApp.File.generate_name/1.
Ex : name = MyApp.File.generate_name “uploads/user/avatars”

The generated file name doesn’t contain the file extension.
I tried to override Arc.Definition.filename function in the file generated by Arc.g avatar.
But when when I add new user with an avatar pic, file stored in disk name differs from name stored in column “avatar” of user schema. The first name (in disk) is what I expect from overrided filename function, and the second name (in database user table) is the initial uploaded pic name.

The other problem I’m facing is that each time the Arc.Definition.filename function is called the name is generated again. I try inside the function to guess when to generate a new name but it doesn’t work well. For each new user insertion it’s called twice. Here is my overriding code :

  # Override the persisted filenames:
  def filename(version, {file, _}) do
    # is it a storage request ?
    if Map.has_key?(file, :path) do
      IO.inspect file
      MyApp.File.generate_name("uploads/user/avatars/#{version}")
    else
      Path.rootname(file.file_name)
    end
  end

Could someone guide me please ?

Marked As Solved

Qqwy

Qqwy

TypeCheck Core Team

I have been fiddling with getting proper filenames in arc/arc_ecto as well.

The final filename function I use right now:

defmodule MyImage do
  use Arc.Definition
  use Arc.Ecto.Definition

  # functions to create thumbnail versions
  # ...

  def filename(version, {file, scope}) do
    # quick fix to prevent https://github.com/stavro/arc/issues/174
    file_name = Path.basename(file.file_name, Path.extname(file.file_name)) 
    "#{scope.uuid}_#{version}_#{file_name}"
  end
end

My model contains a field with the type Ecto.UUID, and in my changeset function I make sure this is set before the cast_attachments call by using

def mymodel_changeset(mymodel, attrs) do
  mymodel
  |> Map.update(:uuid, Ecto.UUID.generate, fn val -> val || Ecto.UUID.generate end)
  |> cast_attachments(attrs, [:image])
  # other validations, casts, etc.
end

Main tip to give to people would be that Ecto already has UUID-generation/handling functionality built in, so using an external library is not required.

Oh, and the approach with using Ecto.Changeset.get_field and force_change is definitely cleaner :smiley: !

Also Liked

Kurisu

Kurisu

@Qqwy
Your reply is very instructive for me. I just tested Ecto.UUID and found it perfect for the job. I will drop the external library. Thanks. :smile:

For the filename function I keep it simple like this :

# Override the persisted filenames:
  def filename(version, _) do
    version
  end

I just worked on the storage_dir function to make unique path :

  # Override the storage directory:
  def storage_dir(_, {file, scope}) do
    "uploads/avatars/#{scope.uuid}"
  end

This way for each image file uploaded, we’ll have a folder named with generated uuid, containing all the versions created (thumb, original…). The point is that when I have to delete an entry in the DB, I can simply remove its uuid corresponding folder. Beside that, I found it simpler to write my own image_delete function in the custom uploader module :

#delete all versions of an avatar
  def remove(scope) do
    storage_dir(nil, {scope.avatar, scope}) |> File.rm_rf!
  end

To be honest I have to say that I was a bit confused with the delete function of arc, so I wrote my simple own. ^^

To finish I don’t know if will have any impact on DB performance but while I’m using an uuid field for arc._ecto usage, I’m still using an Ecto default primary key for the same model. I guess a proper way to do things, would be using the uuid field as primary key in the same time ?

dokicro

dokicro

I ended up doing this:

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

:os.system_time should be always unique if I am not wrong :slight_smile:

OvermindDL1

OvermindDL1

Not ‘always’, but close enough for ‘most’ people not to care. :slight_smile:

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53578 245
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
nobody
Hi! In PHP: $SERVER['SERVERADDR'] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New

We're in Beta

About us Mission Statement