150

I have implemented an algorithm that will generate unique names for files that will save on hard drive. I'm appending DateTime: Hours,Minutes,Second and Milliseconds but still it generates duplicate name of files because im uploading multiple files at a time.

What is the best solution to generate unique names for files to be stored on hard drive so no 2 files are same?

1
  • It depends on other requirements; this [old] question was/is too vague. Commented May 25, 2018 at 20:53

21 Answers 21

270

If readability doesn't matter, use GUIDs.

E.g.:

var myUniqueFileName = string.Format(@"{0}.txt", Guid.NewGuid());

or shorter:

var myUniqueFileName = $@"{Guid.NewGuid()}.txt";

In my programs, I sometimes try e.g. 10 times to generate a readable name ("Image1.png"…"Image10.png") and if that fails (because the file already exists), I fall back to GUIDs.

Update:

Recently, I've also use DateTime.Now.Ticks instead of GUIDs:

var myUniqueFileName = string.Format(@"{0}.txt", DateTime.Now.Ticks);

or

var myUniqueFileName = $@"{DateTime.Now.Ticks}.txt";

The benefit to me is that this generates a shorter and "nicer looking" filename, compared to GUIDs.

Please note that in some cases (e.g. when generating a lot of random names in a very short time), this might make non-unique values.

Stick to GUIDs if you want to make really sure that the file names are unique, even when transfering them to other computers.

4
  • 9
    I like using Ticks as a GUID is really ugly. You can also get a hash of the Ticks which reduces the character length of the filename. DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()
    – WillMcKill
    Commented Feb 20, 2017 at 12:15
  • 9
    "Ticks" is predictable and not thread-safe (as the same 'ticks' can be obtained from multiple threads/processes). This makes it not suitable for temp filename generation. Generating X..1..N may be suitable for user-facing tasks (ie. copy in Explorer), but is dubious for server work. Commented May 24, 2018 at 16:57
  • I think the "@" character in the interpolated strings is redundant. Just use $"{Guid.NewGuid()}.txt" instead. Commented Nov 27, 2023 at 11:19
  • @MustafaÖzçetin Yes, it is. I use if for myself to distinguish strings that do not need to be translated. Very handy visual distinction for me 😊.
    – Uwe Keim
    Commented Nov 28, 2023 at 8:09
97

Use

Path.GetTempFileName()

or use new GUID().

Path.GetTempFilename() on MSDN.

6
  • 1
    Here is the link to the MSDN doc: msdn.microsoft.com/en-us/library/…
    – epotter
    Commented Jan 11, 2011 at 13:56
  • 3
    Note though, that GetTempFileName() might throw an exception if you create many such files without deleting them.
    – Joey
    Commented Jan 11, 2011 at 18:14
  • 25
    "The GetTempFileName method will raise an IOException if it is used to create more than 65535 files without deleting previous temporary files." says the MSDN article. Commented Jan 13, 2011 at 6:37
  • 3
    WARNING: GetTempFileName will create a File. This also means that it chooses the temp path location. On the other hand, GetRandomFileName is suitable to generate a 8.3 filename that can be used with a different path. (I've seen some horrid code that uses GetTempFileName with a File.Delete just to use the filename elsewhere..) Commented May 24, 2018 at 17:04
  • 2
    How did this get upvoted? I mean, do you know how misleading this answer is. It would be a disaster for anyone who doesn't know what Path.GetTempFileName() actually does. Commented Oct 11, 2021 at 6:50
87
System.IO.Path.GetRandomFileName()

Path.GetRandomFileName() on MSDN.

2
  • 1
    Here is the link to the MSDN doc: msdn.microsoft.com/en-us/library/…
    – epotter
    Commented Jan 11, 2011 at 13:57
  • 13
    @RyBolt: Since you don't have to seed yourself there is pretty much nothing you have to keep in mind for using that method. And I'd expect the vast majority of developers to have no clue about how to build secure cryptographic systems.
    – Joey
    Commented Jan 11, 2011 at 18:12
56

If the readability of the file name isn't important, then the GUID, as suggested by many will do. However, I find that looking into a directory with 1000 GUID file names is very daunting to sort through. So I usually use a combination of a static string which gives the file name some context information, a timestamp, and GUID.

For example:

public string GenerateFileName(string context)
{
    return context + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N");
}

filename1 = GenerateFileName("MeasurementData");
filename2 = GenerateFileName("Image");

This way, when I sort by filename, it will automatically group the files by the context string and sort by timestamp.

Note that the filename limit in windows is 255 characters.

6
  • 2
    +1 For the suggestion to include useful information combined with a GUID. - An aside to take with a grain of salt: including the date and time in a file name is kind of redundant when you can just Right Click > Sort By > Date. Commented Jun 9, 2013 at 3:39
  • 1
    The time becomes useful if you're storing a bunch of files with different contexts in the same directory. Of course, the file name generation should be adjusted based on your specific needs.
    – Mas
    Commented Jun 10, 2013 at 12:27
  • Should be Guid.NewGuid().ToString();. Missing parenthesis. +1 otherwise
    – Laurent W.
    Commented Jan 28, 2014 at 11:24
  • This is very slick. Timestamp and Guid. +1 Commented Oct 21, 2015 at 18:17
  • I like this solution +1, I added a second parameter string extension which I add to the fileName, this further supports the idea of context and allows to easily open the file with the default application on double click if necessary Commented Aug 15, 2019 at 6:33
26

Here's an algorithm that returns a unique readable filename based on the original supplied. If the original file exists, it incrementally tries to append an index to the filename until it finds one that doesn't exist. It reads the existing filenames into a HashSet to check for collisions so it's pretty quick (a few hundred filenames per second on my machine), it's thread safe too, and doesn't suffer from race conditions.

For example, if you pass it test.txt, it will attempt to create files in this order:

test.txt
test (2).txt
test (3).txt

etc. You can specify the maximum attempts or just leave it at the default.

Here's a complete example:

class Program
{
    static FileStream CreateFileWithUniqueName(string folder, string fileName, 
        int maxAttempts = 1024)
    {
        // get filename base and extension
        var fileBase = Path.GetFileNameWithoutExtension(fileName);
        var ext = Path.GetExtension(fileName);
        // build hash set of filenames for performance
        var files = new HashSet<string>(Directory.GetFiles(folder));

        for (var index = 0; index < maxAttempts; index++)
        {
            // first try with the original filename, else try incrementally adding an index
            var name = (index == 0)
                ? fileName
                : String.Format("{0} ({1}){2}", fileBase, index, ext);

            // check if exists
            var fullPath = Path.Combine(folder, name);
            if(files.Contains(fullPath))
                continue;

            // try to create the file
            try
            {
                return new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write);
            }
            catch (DirectoryNotFoundException) { throw; }
            catch (DriveNotFoundException) { throw; }
            catch (IOException) 
            {
                // Will occur if another thread created a file with this 
                // name since we created the HashSet. Ignore this and just
                // try with the next filename.
            } 
        }

        throw new Exception("Could not create unique filename in " + maxAttempts + " attempts");
    }

    static void Main(string[] args)
    {
        for (var i = 0; i < 500; i++)
        {
            using (var stream = CreateFileWithUniqueName(@"c:\temp\", "test.txt"))
            {
                Console.WriteLine("Created \"" + stream.Name + "\"");
            }
        }

        Console.ReadKey();
    }
}
3
  • thread-safe ? not static readonly variable neither lock ?
    – Kiquenet
    Commented Mar 21, 2017 at 15:40
  • The method itself is static so shares nothing, so I believe that multiple threads can safely enter this method concurrently. Perhaps thread-safe isn't quite the correct term - I'm trying to convey that if another thread/process creates a file with a conflicting name during execution then this method recovers and tries the next available name. Feel free to edit if you think it can be improved. Commented Mar 22, 2017 at 1:02
  • Maybe "not suffering from a race condition" is a better way of putting it. Commented Apr 10, 2017 at 17:54
14

I use GetRandomFileName:

The GetRandomFileName method returns a cryptographically strong, random string that can be used as either a folder name or a file name. Unlike GetTempFileName, GetRandomFileName does not create a file. When the security of your file system is paramount, this method should be used instead of GetTempFileName.

Example:

public static string GenerateFileName(string extension="")
{
    return string.Concat(Path.GetRandomFileName().Replace(".", ""),
        (!string.IsNullOrEmpty(extension)) ? (extension.StartsWith(".") ? extension : string.Concat(".", extension)) : "");
}
3
  • Does GetRandomFileName() method always generate the unique file name each time similar to GUID()? Commented Jun 22, 2018 at 9:32
  • 1
    @AshishShukla actually I have no idea. msdn says "a cryptographically strong, random string" is generated. I had no problems so far. If the uniqueness is critical, an extra check might be a good idea.
    – Koray
    Commented Jun 25, 2018 at 8:11
  • It should be noted that Microsoft removed the text you quoted for some reason. Now the docs simply say "Returns a random folder name or file name."
    – Bip901
    Commented Oct 20, 2020 at 20:19
4

You can have a unique file name automatically generated for you without any custom methods. Just use the following with the StorageFolder Class or the StorageFile Class. The key here is: CreationCollisionOption.GenerateUniqueName and NameCollisionOption.GenerateUniqueName

To create a new file with a unique filename:

var myFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("myfile.txt", NameCollisionOption.GenerateUniqueName);

To copy a file to a location with a unique filename:

var myFile2 = await myFile1.CopyAsync(ApplicationData.Current.LocalFolder, myFile1.Name, NameCollisionOption.GenerateUniqueName);

To move a file with a unique filename in the destination location:

await myFile.MoveAsync(ApplicationData.Current.LocalFolder, myFile.Name, NameCollisionOption.GenerateUniqueName);

To rename a file with a unique filename in the destination location:

await myFile.RenameAsync(myFile.Name, NameCollisionOption.GenerateUniqueName);
3
  1. Create your timestamped filename following your normal process
  2. Check to see if filename exists
  3. False - save file
  4. True - Append additional character to file, perhaps a counter
  5. Go to step 2
1
  • 10
    This algorithm is vunerable to concurrency
    – Jader Dias
    Commented Jan 11, 2011 at 16:14
2

Do you need the date time stamp in the filename?

You could make the filename a GUID.

4
2

I have been using the following code and its working fine. I hope this might help you.

I begin with a unique file name using a timestamp -

"context_" + DateTime.Now.ToString("yyyyMMddHHmmssffff")

C# code -

public static string CreateUniqueFile(string logFilePath, string logFileName, string fileExt)
    {
        try
        {
            int fileNumber = 1;

            //prefix with . if not already provided
            fileExt = (!fileExt.StartsWith(".")) ? "." + fileExt : fileExt;

            //Generate new name
            while (File.Exists(Path.Combine(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt)))
                fileNumber++;

            //Create empty file, retry until one is created
            while (!CreateNewLogfile(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt))
                fileNumber++;

            return logFileName + "-" + fileNumber.ToString() + fileExt;
        }
        catch (Exception)
        {
            throw;
        }
    }

    private static bool CreateNewLogfile(string logFilePath, string logFile)
    {
        try
        {
            FileStream fs = new FileStream(Path.Combine(logFilePath, logFile), FileMode.CreateNew);
            fs.Close();
            return true;
        }
        catch (IOException)   //File exists, can not create new
        {
            return false;
        }
        catch (Exception)     //Exception occured
        {
            throw;
        }
    }
2

Why can't we make a unique id as below.

We can use DateTime.Now.Ticks and Guid.NewGuid().ToString() to combine together and make a unique id.

As the DateTime.Now.Ticks is added, we can find out the Date and Time in seconds at which the unique id is created.

Please see the code.

var ticks = DateTime.Now.Ticks;
var guid = Guid.NewGuid().ToString();
var uniqueSessionId = ticks.ToString() +'-'+ guid; //guid created by combining ticks and guid

var datetime = new DateTime(ticks);//for checking purpose
var datetimenow = DateTime.Now;    //both these date times are different.

We can even take the part of ticks in unique id and check for the date and time later for future reference.

You can attach the unique id created to the filename or can be used for creating unique session id for login-logout of users to our application or website.

5
  • Why bother with 'ticks' if there is a GUID? Commented May 24, 2018 at 16:58
  • During any scenario, if you need to check when the uniqueSessionId is generated, you will get the exact time. And also at that specific tick will occur only once in lifetime. Commented May 25, 2018 at 6:12
  • Trivially, that assumption about ticks is invalid: 1) multiple observers can see the same 'tick' (think threads/processes) and 2) the same 'tick' can be observed multiple times by the same observer, if queried fast enough. Commented May 25, 2018 at 20:28
  • However, by just using Guid.NewGuid (and ignoring that fact it is not "cryptographically random" which might be of interest in some cases), we can assert that, with a high enough probability that we don't care about otherwise, a unique ID will be generated - this is a much, much higher guarantee than 'ticks'. Thus, the 'ticks' has no value/use here expect as "secondary" data shoved into the filename. Commented May 25, 2018 at 20:30
  • (FWIW: I just fixed some code with a broken aforementioned assertion about 'unique time'..) Commented May 25, 2018 at 20:52
2

DateTime.Now.Ticks is not safe, Guid.NewGuid() is too ugly, if you need something clean and almost safe (it's not 100% safe for example if you call it 1,000,000 times in 1ms), try:

Math.Abs(Guid.NewGuid().GetHashCode())

By safe I mean safe to be unique when you call it so many times in very short period few ms of time.

3
  • Is there any problem with my solution downvoter? please let me know. Commented Apr 20, 2020 at 7:57
  • The GetHashCode method returns an int, which has 32-bit range, whereas a GUID has a 128-bit range, and so is much more likely to be unique. If you don't like the format of a GUID value, simply call ToString("N") on it, which removes the dashes.
    – 4thex
    Commented May 29, 2020 at 16:57
  • 1
    Yes, but as I mentioned Guid.NewGuid() is ugly and long vs GetHashCode(), e.g: 83c5bf1722f64937a01bcf46531ee385 vs 899709107. of course depend on situation the solution will be different, this is my solution which I used in past 6 years in all my projects without any problem. Commented Sep 16, 2020 at 8:50
1

How about using Guid.NewGuid() to create a GUID and use that as the filename (or part of the filename together with your time stamp if you like).

1

I've written a simple recursive function that generates file names like Windows does, by appending a sequence number prior to the file extension.

Given a desired file path of C:\MyDir\MyFile.txt, and the file already exists, it returns a final file path of C:\MyDir\MyFile_1.txt.

It is called like this:

var desiredPath = @"C:\MyDir\MyFile.txt";
var finalPath = UniqueFileName(desiredPath);

private static string UniqueFileName(string path, int count = 0)
{
    if (count == 0)
    {
        if (!File.Exists(path))
        {
            return path;
        }
    }
    else
    {
        var candidatePath = string.Format(
            @"{0}\{1}_{2}{3}",
            Path.GetDirectoryName(path),
            Path.GetFileNameWithoutExtension(path),
            count,
            Path.GetExtension(path));

        if (!File.Exists(candidatePath))
        {
            return candidatePath;
        }
    }

    count++;
    return UniqueFileName(path, count);
}
1
  • This is not thread-safe or process-safe. There is a race-condition with the File.Exists check and any (later supposed) creation of the file. Trivially, when called twice in a row without creating a file it will return the same result. Commented May 24, 2018 at 17:03
0

If you would like to have the datetime,hours,minutes etc..you can use a static variable. Append the value of this variable to the filename. You can start the counter with 0 and increment when you have created a file. This way the filename will surely be unique since you have seconds also in the file.

0

I usually do something along these lines:

  • start with a stem file name (work.dat1 for instance)
  • try to create it with CreateNew
  • if that works, you've got the file, otherwise...
  • mix the current date/time into the filename (work.2011-01-15T112357.dat for instance)
  • try to create the file
  • if that worked, you've got the file, otherwise...
  • Mix a monotonic counter into the filename (work.2011-01-15T112357.0001.dat for instance. (I dislike GUIDs. I prefer order/predictability.)
  • try to create the file. Keep ticking up the counter and retrying until a file gets created for you.

Here's a sample class:

static class DirectoryInfoHelpers
{
    public static FileStream CreateFileWithUniqueName( this DirectoryInfo dir , string rootName )
    {
        FileStream fs = dir.TryCreateFile( rootName ) ; // try the simple name first

        // if that didn't work, try mixing in the date/time
        if ( fs == null )
        {
            string date = DateTime.Now.ToString( "yyyy-MM-ddTHHmmss" ) ;
            string stem = Path.GetFileNameWithoutExtension(rootName) ;
            string ext  = Path.GetExtension(rootName) ?? ".dat" ;

            ext = ext.Substring(1);

            string fn = string.Format( "{0}.{1}.{2}" , stem , date , ext ) ;
            fs = dir.TryCreateFile( fn ) ;

            // if mixing in the date/time didn't work, try a sequential search
            if ( fs == null )
            {
                int seq = 0 ;
                do
                {
                    fn = string.Format( "{0}.{1}.{2:0000}.{3}" , stem , date , ++seq , ext ) ;
                    fs = dir.TryCreateFile( fn ) ;
                } while ( fs == null ) ;
            }

        }

        return fs ;
    }

    private static FileStream TryCreateFile(this DirectoryInfo dir , string fileName )
    {
        FileStream fs = null ;
        try
        {
            string fqn = Path.Combine( dir.FullName , fileName ) ;

            fs = new FileStream( fqn , FileMode.CreateNew , FileAccess.ReadWrite , FileShare.None ) ;
        }
        catch ( Exception )
        {
            fs = null ;
        }
        return fs ;
    }

}

You might want to tweak the algorithm (always use all the possible components to the file name for instance). Depends on the context -- If I was creating log files for instance, that I might want to rotate out of existence, you'd want them all to share the same pattern to the name.

The code isn't perfect (no checks on the data passed in for instance). And the algorithm's not perfect (if you fill up the hard drive or encounter permissions, actual I/O errors or other file system errors, for instance, this will hang, as it stands, in an infinite loop).

0
0

I ends up concatenating GUID with Day Month Year Second Millisecond string and i think this solution is quite good in my scenario

0
0

you can use Random.Next() also to generate a random number. you can see the MSDN link: http://msdn.microsoft.com/en-us/library/9b3ta19y.aspx

1
  • 2
    I would not recommend this, since it doesn't need to be random. It needs to be unique.
    – Max
    Commented Feb 21, 2014 at 8:36
0

I wrote a class specifically for doing this. It's initialized with a "base" part (defaults to a minute-accurate timestamp) and after that appends letters to make unique names. So, if the first stamp generated is 1907101215a, the second would be 1907101215b, then 1907101215c, et cetera.

If I need more than 25 unique stamps then I use unary 'z's to count 25's. So, it goes 1907101215y, 1907101215za, 1907101215zb, ... 1907101215zy, 1907101215zza, 1907101215zzb, and so forth. This guarantees that the stamps will always sort alphanumerically in the order they were generated (as long as the next character after the stamp isn't a letter).

It isn't thread-safe, doesn't automatically update the time, and quickly bloats if you need hundreds of stamps, but I find it sufficient for my needs.

/// <summary>
/// Class for generating unique stamps (for filenames, etc.)
/// </summary>
/// <remarks>
/// Each time ToString() is called, a unique stamp is generated.
/// Stamps are guaranteed to sort alphanumerically in order of generation.
/// </remarks>
public class StampGenerator
{
  /// <summary>
  /// All the characters which could be the last character in the stamp.
  /// </summary>
  private static readonly char[] _trailingChars =
  {
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
    'u', 'v', 'w', 'x', 'y'
  };

  /// <summary>
  /// How many valid trailing characters there are.
  /// </summary>
  /// <remarks>Should always equal _trailingChars.Length</remarks>
  public const int TRAILING_RANGE = 25;

  /// <summary>
  /// Maximum length of the stamp. Hard-coded for laziness.
  /// </summary>
  public const int MAX_LENGTH_STAMP = 28;

  /// <summary>
  /// Base portion of the stamp. Will be constant between calls.
  /// </summary>
  /// <remarks>
  /// This is intended to uniquely distinguish between instances.
  /// Default behavior is to generate a minute-accurate timestamp.
  /// </remarks>
  public string StampBase { get; }

  /// <summary>
  /// Number of times this instance has been called.
  /// </summary>
  public int CalledTimes { get; private set; }

  /// <summary>
  /// Maximum number of stamps that can be generated with a given base.
  /// </summary>
  public int MaxCalls { get; }

  /// <summary>
  /// Number of stamps remaining for this instance.
  /// </summary>
  public int RemainingCalls { get { return MaxCalls - CalledTimes; } }

  /// <summary>
  /// Instantiate a StampGenerator with a specific base.
  /// </summary>
  /// <param name="stampBase">Base of stamp.</param>
  /// <param name="calledTimes">
  /// Number of times this base has already been used.
  /// </param>
  public StampGenerator(string stampBase, int calledTimes = 0)
  {
    if (stampBase == null)
    {
      throw new ArgumentNullException("stampBase");
    }
    else if (Regex.IsMatch(stampBase, "[^a-zA-Z_0-9 \\-]"))
    {
      throw new ArgumentException("Invalid characters in Stamp Base.",
                                  "stampBase");
    }
    else if (stampBase.Length >= MAX_LENGTH_STAMP - 1)
    {
      throw new ArgumentException(
        string.Format("Stamp Base too long. (Length {0} out of {1})",
                      stampBase.Length, MAX_LENGTH_STAMP - 1), "stampBase");
    }
    else if (calledTimes < 0)
    {
      throw new ArgumentOutOfRangeException(
        "calledTimes", calledTimes, "calledTimes cannot be negative.");
    }
    else
    {
      int maxCalls = TRAILING_RANGE * (MAX_LENGTH_STAMP - stampBase.Length);
      if (calledTimes >= maxCalls)
      {
        throw new ArgumentOutOfRangeException(
          "calledTimes", calledTimes, string.Format(
            "Called Times too large; max for stem of length {0} is {1}.",
            stampBase.Length, maxCalls));
      }
      else
      {
        StampBase = stampBase;
        CalledTimes = calledTimes;
        MaxCalls = maxCalls;
      }
    }
  }

  /// <summary>
  /// Instantiate a StampGenerator with default base string based on time.
  /// </summary>
  public StampGenerator() : this(DateTime.Now.ToString("yMMddHHmm")) { }

  /// <summary>
  /// Generate a unique stamp.
  /// </summary>
  /// <remarks>
  /// Stamp values are orered like this:
  /// a, b, ... x, y, za, zb, ... zx, zy, zza, zzb, ...
  /// </remarks>
  /// <returns>A unique stamp.</returns>
  public override string ToString()
  {
    int zCount = CalledTimes / TRAILING_RANGE;
    int trailing = CalledTimes % TRAILING_RANGE;
    int length = StampBase.Length + zCount + 1;

    if (length > MAX_LENGTH_STAMP)
    {
      throw new InvalidOperationException(
        "Stamp length overflown! Cannot generate new stamps.");
    }
    else
    {
      CalledTimes = CalledTimes + 1;
      var builder = new StringBuilder(StampBase, length);
      builder.Append('z', zCount);
      builder.Append(_trailingChars[trailing]);
      return builder.ToString();
    }
  }
}
0

Old question, I know, but here's is what works for me. If multiple threads download files, assign each a unique number and prepend to it the filename, e.g. 01_202107210938xxxx

0

If you're wanting to generate a file name based off of some text like a DateTime and maybe a GUID, I have made NuGet package that allows you to do this, if you count the amount of filenames you can use that as the seed so that it is truly random. I tried to make it as straight forward and as easy to use as possible, but here's some code that you can use to generate it:

List<string> list = new List<string>();
list.Add(new DateTime().ToString());
list.Add("Some filename or GUID");
int amountoffiles = 5000;
string final_filename = vuniqueit.Identity.GenerateUUID(list, amountoffiles));

Not the answer you're looking for? Browse other questions tagged or ask your own question.