Pracuję nad oprogramowaniem serwera, które okresowo potrzebuje zapisać dane na dysku. Muszę się upewnić, że stary plik zostanie nadpisany i że plik nie może zostać uszkodzony (na przykład tylko częściowo nadpisany) w razie nieoczekiwanych okoliczności.Niezawodne zapisywanie plików (File.Replace) w pracowitym środowisku
Mam przyjął następującą wzoru:
string tempFileName = Path.GetTempFileName();
// ...write out the data to temporary file...
MoveOrReplaceFile(tempFileName, fileName);
... gdzie MoveOrReplaceFile jest:
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);
}
}
Działa to dobrze, o ile serwer ma wyłączny dostęp do plików. Jednak File.Replace wydaje się być bardzo wrażliwy na zewnętrzny dostęp do plików. Za każdym razem mój program działa na systemie z programu antywirusowego lub systemu tworzenia kopii zapasowych w czasie rzeczywistym, losowe błędy File.Replace zaczynają pojawiać się:
System.IO.IOException: Nie można usunąć plik należy wymienić.
Oto kilka możliwych przyczyn, które mam wyeliminowane: uchwyty
- Niepublikowany plik: using() zapewnia, że wszystkie uchwyty plików są uwalniane jak najszybciej.
- Problemy z wątkami: lock() chroni dostęp do każdego pliku.
- Różne woluminy dyskowe: Funkcja File.Replace() kończy się niepowodzeniem, gdy używana jest na woluminach dyskowych. Moja metoda sprawdza to już i wraca do File.Copy().
A oto kilka wskazówek, które natknąłem, i dlatego raczej nie ich użyć:
- Volume Shadow Copy Service: To działa tylko tak długo, jak problematyczne trzeciej oprogramowanie stronnicze (monitory kopii zapasowych i antywirusowych itp.) również używają VSS. Korzystanie z VSS wymaga tonów P/Invoke i ma problemy związane z platformą.
- Blokowanie plików: W C# blokowanie pliku wymaga zachowania otwartego strumienia FileStream. Zachowałoby to oprogramowanie innych firm, ale 1) nadal nie będę w stanie zastąpić pliku za pomocą File.Replace, i 2) Jak wspomniałem powyżej, wolałbym najpierw napisać do pliku tymczasowego, aby uniknąć przypadkowego korupcja.
Byłbym wdzięczny za wszelkie dane wejściowe dotyczące pobierania pliku.Replace do pracy za każdym razem lub, bardziej ogólnie, niezawodnie zapisywania/zastępowania plików na dysku.
Czy można oczekiwać 'MoveOrReplaceFile' być uruchamiane jednocześnie (co oznacza dostęp przez wielu wątków aplikacji * * lub nawet z wieloma instancjami aplikacji)? –
Mój własny kod nigdy nie wywołuje operacji MoveOrReplaceFile jednocześnie, ale są inne procesy poza moją kontrolą, które mogą próbować odczytać plik. Te przypadkowe dostępu do odczytu powodują niepowodzenie File.Replace. – matvei