Надежное сохранение файлов (File.Replace) в загруженной среде - PullRequest
25 голосов
/ 22 января 2012

Я работаю над серверным программным обеспечением, которое периодически должно сохранять данные на диск. Мне нужно убедиться, что старый файл перезаписан и что файл не может быть поврежден (например, только частично перезаписан) в случае непредвиденных обстоятельств.

Я принял следующий шаблон:

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: Невозможно удалить файл, подлежащий замене.

Вот несколько возможных причин, которые я устранил:

  • Неизданные файловые дескрипторы : использование () гарантирует, что все файловые дескрипторы будут освобождены как можно скорее.
  • Проблемы с потоками : lock () защищает весь доступ к каждому файлу.
  • Различные тома диска : File.Replace () завершается ошибкой при использовании на разных дисковых томах. Мой метод уже проверяет это и возвращается к File.Copy ().

И вот несколько советов, с которыми я столкнулся, и почему я бы не стал их использовать:

  • Служба теневого копирования томов : работает только в том случае, если проблемное стороннее программное обеспечение (средства резервного копирования, антивирусные мониторы и т. Д.) Также используют VSS. Использование VSS требует тонны P / Invoke и имеет проблемы с платформой.
  • Блокировка файлов : В C # блокировка файла требует сохранения FileStream открытым. Это не позволит использовать стороннее программное обеспечение, но 1) я все равно не смогу заменить файл с помощью File.Replace, и 2) как я уже упоминал выше, я предпочел бы сначала записать во временный файл, чтобы избежать случайного коррупция.

Буду признателен за любую информацию о том, чтобы заставить File.Replace работать каждый раз или, в более общем смысле, надежно сохранять / перезаписывать файлы на диск.

Ответы [ 3 ]

26 голосов
/ 23 января 2012

Вы действительно хотите использовать третий параметр, имя файла резервной копии.Это позволяет 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 голосов
/ 22 января 2012

Существует несколько возможных подходов, вот некоторые из них:

  1. Использовать файл «блокировки» - временный файл, который создается перед операцией и указывает другим авторам (или читателям), что файлв настоящее время изменяется и, следовательно, исключительно заблокирован.После завершения операции - удалите файл блокировки.В этом методе предполагается, что команда создания файла является атомарной.
  2. Используйте транзакционный API NTFS (при необходимости).
  3. Создайте ссылку на файл, запишите измененный файл под произвольным именем (дляпример Guid.NewGuid ()) - и затем переназначить ссылку на новый файл.Все читатели получат доступ к файлу по ссылке (имя которой известно).

Конечно, все 3 подхода имеют свои недостатки и преимущества

1 голос
/ 22 января 2012

Если программное обеспечение выполняет запись в раздел NTFS, попробуйте использовать Транзакционный NTFS . Вы можете использовать AlphFS для оболочки .NET для API. Это, вероятно, самый надежный способ записи файлов и предотвращения коррупции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...