Jack
Jack

Reputation: 10037

.Net: moving file from one directory to another and avoid the name collision

How do you move a file after generating it to another folder without having name collision problem?

for example, i generate a CSV file name foo_20090820.csv and if i run the program again later on 08/20/2009 then the file will be foo_20090820.csv since on my target folder there already an file generated before the second file. The program will run into an error.

I tried looking into the target folder for the same file name and add some random number in front the second file but it didn't work quite well.

any idea?

Upvotes: 0

Views: 808

Answers (5)

Neil
Neil

Reputation: 7437

I took the liberty of evolving the prior solutions a bit more. Most IOExceptions will need to be handled by the caller.

private static string moveFileWithoutCollisions(string newDirectory, string filePath)
{
    string fileName = Path.GetFileName(filePath);
    string newFilePath = Path.Combine(newDirectory, fileName);
    string bareFileName = Path.GetFileNameWithoutExtension(fileName);
    string extension = Path.GetExtension(fileName);

    while (true)
    {
        int suffix = 0;
        while (File.Exists(newFilePath))
        {
            suffix++;
            string fileNameWithSuffix = bareFileName + suffix + extension;
            newFilePath = Path.Combine(newDirectory, fileNameWithSuffix);
        }

        try
        {
            File.Move(filePath, newFilePath);
        }
        catch (IOException)
        {
            if (File.Exists(newFilePath))
            {
                // Race condition: someone put a file there with the same name. Find a new name.
                continue;
            }
            else
            {
                throw;
            }
        }

        break;
    }

    return newFilePath;
}

Upvotes: 0

Steve Guidi
Steve Guidi

Reputation: 20200

Here is an improvement to EBGreen's solution, which avoids the race condition between checking for the existence of and creating the file.

bool SafeMove(string sourceFilename, string destFilename)
{
    for (int i = 0; i < RetryThreshold; ++i)
    {
        try
        {
            File.Move(sourceFilename, destFilename);
            return true;
        }
        catch(IOException)
        {
            destFilename =
                Path.GetFilenameWithoutExtension(destFilename) + i.ToString() +
                Path.GetExtension(destFilename);
        }
    }

    return false;
}

There are some problems with this code, but it demonstrates the attempt to retry after the Move() operation fails. You should definitely perform some smarter exception handling since not all IOExceptions imply a reason for retrying. Also, returning false is not wise since we lose the error context.

Alternatively, if you're not concerned with the readability of directory or filename, use JP's solution of appending a Guid to filename.

Upvotes: 0

JP Alioto
JP Alioto

Reputation: 45117

If you want absolutely no chance of a collision, you could tack on a GUID.

Upvotes: 2

Jay Riggs
Jay Riggs

Reputation: 53595

Another idea would be to encode the time in the file name. I already see you have the date encoded; what I'm suggesting is tacking on the time.

So you'd end up with something like this:

foo_yyyyMMdd_HHmmss (plus .csv of course)

which would add hours (24 hour clock format), minutes and seconds to your name.

Note that not only would you be able to sort these files in the order they were moved, but their filenames would tell you when they were moved.

Upvotes: 1

EBGreen
EBGreen

Reputation: 37720

Here is pseudo code of how I handle this:

newName = "foo_20090820.csv"
i = 1
while file exists newName
    newName = "foo_20090820-" + i.ToString() + ".csv
    i++
While Loop
Move oldFile to newName

Upvotes: 4

Related Questions