Reputation: 10037
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
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
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
Reputation: 45117
If you want absolutely no chance of a collision, you could tack on a GUID.
Upvotes: 2
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
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