2012-01-22 13 views
24

Sto lavorando su un software server che necessita periodicamente di salvare i dati su disco. Devo assicurarmi che il vecchio file venga sovrascritto e che il file non possa essere danneggiato (ad esempio, solo parzialmente sovrascritto) in caso di circostanze impreviste.Salvataggio di file affidabile (File.Replace) in un ambiente occupato

ho adottato il seguente schema:

string tempFileName = Path.GetTempFileName(); 
// ...write out the data to temporary file... 
MoveOrReplaceFile(tempFileName, fileName); 

... dove 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); 
    } 
} 

Questo metodo funziona bene fino a quando il server ha l'accesso esclusivo ai file. Tuttavia, File.Replace sembra essere molto sensibile all'accesso esterno ai file. Ogni volta che il mio software gira su un sistema con un antivirus o un sistema di backup in tempo reale, gli errori casuali File.Replace avvia popping up:

System.IO.IOException: impossibile rimuovere il file da sostituire.

Ecco alcune possibili cause che ho eliminato:

  • file di Unreleased gestisce: usando() assicura che tutti gli handle di file vengono rilasciati al più presto possibile.
  • Problemi di filettatura: lock() protegge tutti gli accessi a ciascun file.
  • Diversi volumi del disco: File.Replace() ha esito negativo se utilizzato su volumi del disco. Il mio metodo lo controlla già e torna a File.Copy().

e qui ci sono alcuni suggerimenti che ho incontrato, e perché preferirei non li uso:

  • Volume Shadow Copy Service: questo funziona solo fino a quando la terza problematica Anche i software di terze parti (backup e antivirus, ecc.) utilizzano VSS. L'utilizzo di VSS richiede tonnellate di P/Invoke e presenta problemi specifici della piattaforma.
  • Blocco dei file: In C#, il blocco di un file richiede la gestione di un FileStream aperto. Manterrebbe fuori software di terze parti, ma 1) Non riesco ancora a sostituire il file usando File.Replace, e 2) Come ho detto sopra, preferisco scrivere prima su un file temporaneo, per evitare accidentalmente corruzione.

Apprezzerei qualsiasi input su come ottenere File.Replace per funzionare ogni volta o, più in generale, il salvataggio/sovrascrittura di file su disco in modo affidabile.

+1

Si aspetta che "MoveOrReplaceFile" venga eseguito contemporaneamente (ovvero a cui si accede da più thread * dell'applicazione * o anche da più istanze dell'applicazione)? –

+1

Il mio stesso codice non chiama MoveOrReplaceFile contemporaneamente, ma ci sono altri processi fuori dal mio controllo che potrebbero tentare di leggere il file. Questi accessi in lettura casuale sono ciò che causa il fallimento di File.Replace. – matvei

risposta

26

Voi davvero desidera utilizzare la 3 ° parametro, il nome del file di backup. Ciò consente a Windows di rinominare semplicemente il file originale senza doverlo eliminare. L'eliminazione non andrà a buon fine se qualsiasi altro processo ha il file aperto senza eliminare la condivisione, la ridenominazione non è mai un problema. Potresti quindi cancellarlo da solo dopo la chiamata a Sostituisci() e ignorare un errore. Eliminalo anche prima della chiamata a Sostituisci(), in modo che la rinomina non fallisca e pulirai i precedenti tentativi falliti. Quindi approssimativamente:

string backup = destination + ".bak"; 
File.Delete(backup); 
File.Replace(source, destination, backup, true); 
try { 
    File.Delete(backup); 
} 
catch { 
    // optional: 
    filesToDeleteLater.Add(backup); 
} 
+0

Hans, potresti mostrare il codice per questo modello? Questo potrebbe quindi diventare la risposta canonica per questa domanda in futuro. –

+0

Questo è esattamente quello che stavo cercando! Grazie mille. – matvei

+2

Questo non risolve il problema di un livello? Cosa succede se il file di backup non può essere cancellato? – grantnz

1

Se il software sta scrivendo su una partizione NTFS, provare a utilizzare Transactional NTFS. È possibile utilizzare AlphFS per un wrapper .NET per l'API. Questo è probabilmente il modo più affidabile per scrivere file e prevenire la corruzione.

+0

Funziona solo su Vista e versioni successive, però. – CodesInChaos

+1

'File.Replace()' in mono magicamente fa cose come usare POSIX 'rename()' su altri sistemi operativi. Non allontanarsi dalle classi integrate potrebbe complicare la portabilità? Inoltre, se 'File.Replace()' ha effettivamente funzionato come documentato e affidabile, fornisce già le funzionalità necessarie ... – binki

2

Ci sono diversi approcci possibili, qui alcuni di loro:

  1. utilizzare un file "lock" - un file temporaneo che viene creato prima dell'operazione e indica altri scrittori (o lettori) che il file sia modificato e quindi esclusivamente bloccato.Al termine dell'operazione, rimuovere il file di blocco. Questo metodo presuppone che il comando per la creazione di file sia atomico.
  2. Utilizzare l'API transazionale NTFS (se appropriato).
  3. Creare un collegamento al file, scrivere il file modificato sotto un nome casuale (ad esempio Guid.NewGuid()) - e quindi rimappare il collegamento al nuovo file. Tutti i lettori accederanno al file attraverso il link (il cui nome è noto).

Naturalmente tutti e 3 gli approcci hanno i loro vantaggi e svantaggi

Problemi correlati