Надежное сохранение файлов (File.Replace) в загруженной среде
Я работаю над программным обеспечением сервера, которое периодически нужно сохранять данные на диск. Мне нужно убедиться, что старый файл перезаписан и что файл не может быть поврежден (например, только частично перезаписан) в случае непредвиденных обстоятельств.
Я принял следующий шаблон:
string tempFileName = Path.GetTempFileName();
// ...write out the data to temporary file...
MoveOrReplaceFile(tempFileName, fileName);
... где MoveOrReplaceFile:
public static void MoveOrReplaceFile( string source, string destination ) {
if (source == null) throw new ArgumentNullException("source");
if (destination == null) throw new ArgumentNullException("destination");
if (File.Exists(destination)) {
// File.Replace does not work across volumes
if (Path.GetPathRoot(Path.GetFullPath(source)) == Path.GetPathRoot(Path.GetFullPath(destination))) {
File.Replace(source, destination, null, true);
} else {
File.Copy(source, destination, true);
}
} else {
File.Move(source, destination);
}
}
Это работает хорошо, пока сервер имеет эксклюзивный доступ к файлам. Однако File.Replace очень чувствителен к внешнему доступу к файлам. В любое время, когда мое программное обеспечение работает в системе с антивирусом или системой резервного копирования в реальном времени, появляются случайные ошибки File.Replace:
System.IO.IOException: невозможно удалить файл для замены.
Вот некоторые возможные причины, которые я устранил:
- Неидентифицированные дескрипторы файлов: using() гарантирует, что все дескрипторы файлов будут выпущены как можно скорее.
- Проблемы с потоками: lock() защищает весь доступ к каждому файлу.
- Различные тома диска. Ошибка File.Replace() при использовании на томах диска. Мой метод проверяет это уже и возвращается к File.Copy().
И вот несколько советов, которые я встретил, и почему я не хочу их использовать:
- Служба теневого копирования томов. Это работает только в том случае, если проблемное стороннее программное обеспечение (резервные и антивирусные мониторы и т.д.) также использует VSS. Использование VSS требует тонны P/Invoke и имеет проблемы с платформой.
- Блокировка файлов. В С# блокировка файла требует сохранения FileStream. Он сохранит стороннее программное обеспечение, но 1) я все равно не смогу заменить файл с помощью File.Replace и 2) Как я уже упоминал выше, я бы предпочел сначала написать во временный файл, чтобы избежать случайного коррупция.
Я был бы признателен за любые данные о том, как заставить File.Replace работать каждый раз или, в более общем плане, надежно сохранять/перезаписывать файлы на диске.
Ответы
Ответ 1
Вы действительно хотите использовать третий параметр - имя файла резервной копии. Это позволяет Windows просто переименовывать исходный файл без необходимости его удаления. Удаление будет неудачно, если какой-либо другой процесс откроет файл без совместного использования, переименование никогда не будет проблемой. Затем вы можете удалить его самостоятельно после вызова Replace() и проигнорировать ошибку. Также удалите его перед вызовом Replace(), чтобы переименование не получилось, и вы очистите неудачные попытки. Так грубо:
string backup = destination + ".bak";
File.Delete(backup);
File.Replace(source, destination, backup, true);
try {
File.Delete(backup);
}
catch {
// optional:
filesToDeleteLater.Add(backup);
}
Ответ 2
Существует несколько возможных подходов: некоторые из них:
- Используйте файл "lock" - временный файл, созданный до операции, и указывает другим писателям (или читателям), что файл изменяется и, следовательно, исключительно заблокирован. После завершения операции - удалите файл блокировки. Этот метод предполагает, что команда создания файла является атомарной.
- Использовать API транзакций NTFS (при необходимости).
- Создайте ссылку на файл, напишите измененный файл под случайным именем (например, Guid.NewGuid()), а затем перетащите ссылку на новый файл. Все читатели получат доступ к файлу по ссылке (имя которой известно).
Конечно, все 3 подхода имеют свои недостатки и преимущества.
Ответ 3
Если программное обеспечение записывает в раздел NTFS, попробуйте использовать Transactional NTFS. Вы можете использовать AlphFS для .NET-оболочки API. Это, вероятно, самый надежный способ записи файлов и предотвращения коррупции.