How do you safely save data to a file that already exists in C#? I have some data that is serialized to a file and I'm pretty sure is not a good idea to safe directly to the file because if anything goes wrong the file will get corrupted and the previous version will get lost.
So this is what I've been doing so far:
string tempFile = Path.GetTempFileName();
using (Stream tempFileStream = File.Open(tempFile, FileMode.Truncate))
{
SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
xmlFormatter.Serialize(tempFileStream, Project);
}
if (File.Exists(fileName)) File.Delete(fileName);
File.Move(tempFile, fileName);
if (File.Exists(tempFile)) File.Delete(tempFile);
The problem is that when I tried to save to a file that was in my Dropbox, sometimes I got an exception telling me that it cannot save to a file that already exists. Apparently the first File.Delete(fileName);
didn't delete the file right away but after a little bit. So I got an exception in the File.Move(tempFile, fileName);
because the file existed and then the file got erased and my file got lost.
I've used other applications with files in my Dropbox and somehow they manage to not mess it up. When I'm trying to save to a file in my Dropbox folder, sometimes I get a message telling me that the file is being used or stuff like that but I never had a problem with a file being erased.
So what would it be the standard / best practice here?
OK this is what I came up with after reading all answers:
private string GetTempFileName(string dir)
{
string name = null;
int attempts = 0;
do
{
name = "temp_" + Player.Math.RandomDigits(10) + ".hsp";
attempts++;
if (attempts > 10) throw new Exception("Could not create temporary file.");
}
while (File.Exists(Path.Combine(dir, name)));
return name;
}
private void SaveProject(string fileName)
{
bool originalRenamed = false;
string tempNewFile = null;
string oldFileTempName = null;
try
{
tempNewFile = GetTempFileName(Path.GetDirectoryName(fileName));
using (Stream tempNewFileStream = File.Open(tempNewFile, FileMode.CreateNew))
{
SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
xmlFormatter.Serialize(tempNewFileStream, Project);
}
if (File.Exists(fileName))
{
oldFileTempName = GetTempFileName(Path.GetDirectoryName(fileName));
File.Move(fileName, oldFileTempName);
originalRenamed = true;
}
File.Move(tempNewFile, fileName);
originalRenamed = false;
CurrentProjectPath = fileName;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if(tempNewFile != null) File.Delete(tempNewFile);
if (originalRenamed) MessageBox.Show("'" + fileName + "'" +
" have been corrupted or deleted in this operation.\n" +
"A backup copy have been created at '" + oldFileTempName + "'");
else if (oldFileTempName != null) File.Delete(oldFileTempName);
}
}
Player.Math.RandomDigits
is just a little function I made that creates an string with n random digits.
I don't see how could this mess up the original file unless the OS is going wacko. It's pretty close to Hans's answer except that I first save the file to a temporary file so that, if something goes wrong when serializing, I don't need to rename the file back to it's original name, which can also go wrong. Please! let me know if you find any flaw.
I'm not sure how secure this is, but assuming your OS isn't crashing, guess what? There's an app for that: File.Replace
File.Replace(tempFile, fileName, backupFileName);
I think what you really need in a critical situation is transactions; only then can you guarantee against the loss of data. Take a look at this article for a .NET solution, but be aware that it might be a bit harder to use than a simple file replacement solution.
This is the way I usually do it:
hello.dat
) with an auto-increment serial number attached, or the time attached (just make sure that it is unique, so use ticks or microseconds etc.) -- say hello.dat.012345
. If that file exists, generate another number and try again.hello.dat
-> hello.dat.bak.023456
hello.dat
If there is any failure anywhere between #3 and #8, send a warning message. You're never losing any data, but temp files may build up. Clean your directory once in a while.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With