Hey guys!
Happy New Year! I am glad to be back this year with some interesting news. Thank you for blasting my mailbox with your questions and suggestions! I will do my best to answer all of them as soon as I can!
One of the readers asked me a question that I think is very interesting. Moreover, I have faced the same issue recently, and today I want to share it with you.
So topic of today is:
How to ensure filename is always unique ? [Ruby]
Why is this a problem?
Imagine you are copying a file ‘a.jpg’ in the directory where this file already exist. Some of the systems I know just give you a validation error and ask to rename a file. But it is a good user experience ? I don’t think so.
So what are our options to tackle this issue ?
Option 1. Append some random string to the filename e.g ‘a-asdsds.jpg’
But is it a solution? How random are those strings ? Does this look appealing ?
I think it is a solution, but a pretty ugly one.
Option 2. Append a number to the filename that is incremented with every creation
This solutions is definitely not a new one, it is used by some OS, and I think it might be the easiest and most elegant one. E.g ‘picture.png’ becomes ‘picture-1.png’ and then ‘picture-2.png’.
Any other options you can think of ? Please comment and let me know ;)
Meanwhile we will go with Option 2.
Next question is:
Based on what should we increment this number ?
Some of the solutions I have seen before is based on counting number of files, and then incrementing count by one. This makes sense, but unfortunately this won’t work. ;)
Why?
Lets say I have files ‘a-1.jpg’, ‘a-2.jpg’,a-3.jpg’. Count is 3 here. Now Imagine I have deleted file ‘a-2.jpg’, and I want to add a new file. So current Count is 2 (a-1 and a-3), so my next filename based on logic should be a-3 (we increment count by 1 -> 2+1), but it is already taken.
Here is the strategy that I think might work:
Lets say we again have 3 files – ‘a-1.jpg’, ‘a-2.jpg’,a-3.jpg’. We will take the numbers of those files (1,2,3) find the largest and increment it by one. So if we apply to it to previous use-case – we will delete ‘a-2′, but largest number is still 3, so new filename will be ‘a-4′.
What do you think ?
I know that most of you are code-maniacs and want to dig into code right away, so I will shut up now and show you some code I have built to do exactly this :)
# Function generates uniq file name from the String passed to it # based on extension and basename # # @param [String] new_file_name desired file_name # # @return [String] generated file name def self.generate_random_filename(filename) ext = File.extname(filename) name = File.basename(filename, ext) related_file_indexes = [] @file_list.select do |file| if File.basename(file).include?(name) && File.extname(file) == ext related_file_indexes << file.split("-").last.to_i end end return name + '-' + (related_file_indexes.max + 1).to_s + ext end
Short Explanation:
1. I take the file lets say ‘a.jpg’, and divide it into ‘a’ as a name and ‘.jpg’ as an extension.
2. I have a @file_list array that is pre-populated with filenames in the directory lets say @file_list is [‘a-1.jpg’,’a-3.jpg’, ‘a.jpg’,’a-1.png’] .
3. I check if filename in @file_list consist of ‘a’ and with extension ‘.jpg’, thus I get 3 files [‘a-1.jpg’,’a.jpg’,’a-3.jpg’]
4. I have another array ‘related_file_indexes’, that splits every element of an array by last ‘-‘ and returns the number which gives me [‘1′,’3′]
5. I built filename with name, max of 1 and 3 which is 3+1=4 and append extension so my output will be ‘a-4.jpg‘
Full version of this code with unit tests can be found here .
Once again, all suggestions and recommendation are more than welcome in the comments to this post ;)
Have a wonderful day!
Anatoly
Tagged: filename, files, open-source, Rspec, Ruby, unique file name
