How to ensure that filename is always unique ? [Ruby]


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

Advertisements

About Anatoly Spektor

My name is Anatoly Spektor (originally Anatolijs Spektors) I am Software and Web Developer. I have worked in Seneca Center for Development of Open Technology on Big Blue Button Add-on - Polling Module, Red Hat and some other places :) I am an author of the book 'Eclipse Debugging How To', Muay Thai fighter and amateur photographer ;)
This entry was posted in Ruby, Useful Functions and tagged , , , , , . Bookmark the permalink.

2 Responses to How to ensure that filename is always unique ? [Ruby]

  1. Nop says:

    Tempfile.new or SecureRandom.hex
    If you really want to keep the name-I convention, then keep a simple text file with a counter and RENAME a temporary file to name-I. rename is an atomic operation.

  2. Hey Nop,

    Both Tempfile.new and SecureRandom.hex – definitely work if you want to create brand new random file.

    The use case I was showing here was when you are having file ‘test’ and you are copying it to the directory where it already exists, I don’t think you would expect to have file name ’12sdsdsds’ (or other hex string) after copying file ‘test’ 🙂

    Having a simple text file with a counter – would work as well – very good suggestion!

    As well as having a settings db table that keeps that kind of stuff.

    I personally think that Reading a file every time you want to rename it is too expensive.

    Thanks a lot for your suggestions!

    Anatoly

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s