既存のファイルに内容を書きこむ時に、直接対象のファイルを開いて書き込みを行うとアプリが強制終了するなどでストリームが異常終了するとファイルの内容が破損する場合があります。この問題を避けるためには以下のアプローチが必要です。
- 直接ファイルを開かない
- 別の場所に書き出してからファイルを保存先に移動する
ですが、この処理を毎回書くのは多少面倒なので、簡単に上記動作を実行できる Utility を作成してみました。
using System;
using System.IO;
public static class FileUtility
{
public static void SaveNew(string savePath, Action<string> saveAction)
{
string tempPath = "";
try
{
tempPath = GetUniquePath();
saveAction(tempPath);
Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(tempPath, savePath, true);
}
finally
{
if (File.Exists(tempPath))
{
File.Delete(tempPath);
}
}
}
public static void Append(string filePath, Action<string> appendAction)
{
if (!File.Exists(filePath))
{
SaveNew(filePath, appendAction);
}
else
{
string tempPath = "";
try
{
tempPath = GetUniquePath();
File.Copy(filePath, tempPath, true);
appendAction(tempPath);
Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(tempPath, filePath, true);
}
finally
{
if (File.Exists(tempPath))
{
File.Delete(tempPath);
}
}
}
}
public static string GetUniquePath()
{
string filePath;
do
{
filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".tmp");
}
while (File.Exists(filePath));
return filePath;
}
}
使い方は以下の通りです。
using System;
using System.IO;
using System.Text;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
FileUtility.Save(@"d:\sample1.txt",
tmpPath =>
{
var sw = new StreamWriter(tmpPath, false, Encoding.UTF8);
sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
});
FileUtility.Save(@"d:\sample2.txt", Save);
}
private static void Save(string filePath)
{
var sw = new StreamWriter(filePath, false, Encoding.UTF8);
sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
}
}
これでファイルを開きっぱなしでアプリがクラッシュしてデータが全部消えるという状態を少しは避けることができると思います。