Überschreiben einer vorhandenen Datei in C #

69761
Juan

Ich habe gerade herausgefunden, dass es dieses Forum gibt und es ist genau das, was ich brauchte. Ich habe gerade diese Frage zu Stack Overflow gestellt und nachdem ich die Antworten gelesen hatte, kam ein Code (nicht der, den ich dort gepostet habe, sondern der, den ich hier poste).

Dies ist zunächst mein Code, der durcheinander gebracht wird:

string tempFile = Path.GetTempFileName();

using (Stream tempFileStream = File.Open(tempFile, FileMode.Truncate))
{
    SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
    xmlFormatter.Serialize(tempFileStream, Project);
}

if (File.Exists(fileName)) File.Delete(fileName);
File.Move(tempFile, fileName);
if (File.Exists(tempFile)) File.Delete(tempFile);

Beim Speichern in einer vorhandenen Datei, die sich in meiner Dropbox befindet, wird manchmal die Originaldatei gelöscht und die temporäre Datei an den ursprünglichen Speicherort in verschoben File.Move(tempFile, fileName);. Also hier ist mein neuer Code, den ich meiner Meinung nach niemals tun sollte, wenn das Betriebssystem nicht selbstbewusst und böse geworden ist, die ursprüngliche Datei löschen, ohne die neue zu speichern. Das Schlimmste, was passieren kann, ist, dass das Original umbenannt wird und so bleibt, und ich muss den Benutzer darüber informieren (siehe MessageBoxunten):

private string GetTempFileName(string dir)
{
    string name = null;
    int attempts = 0;
    do
    {
        name = "temp_" + Player.Math.RandomDigits(10) + ".hsp";
        attempts++;
        if (attempts > 10) throw new Exception("Could not create temporary file.");
    }
    while (File.Exists(Path.Combine(dir, name)));

    return name;
}

private void TryToDelete(string path)
{
    try { File.Delete(path); }
    catch { }
}

private void SaveProject(string fileName)
{
    bool originalRenamed = false;
    string tempNewFile = null;
    string oldFileTempName = null;
    Exception exception = null;

    try
    {
        tempNewFile = GetTempFileName(Path.GetDirectoryName(fileName));

        using (Stream tempNewFileStream = File.Open(tempNewFile, FileMode.CreateNew))
        {
            SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
            xmlFormatter.Serialize(tempNewFileStream, Project);
        }

        if (File.Exists(fileName))
        {
            oldFileTempName = GetTempFileName(Path.GetDirectoryName(fileName));
            File.Move(fileName, oldFileTempName);
            originalRenamed = true;
        }

        File.Move(tempNewFile, fileName);
        originalRenamed = false;

        CurrentProjectPath = fileName;
    }
    catch (Exception ex)
    {
        exception = ex;
    }
    finally
    {
        if (tempNewFile != null) TryToDelete(tempNewFile);

        if (originalRenamed)
        {
            try
            {
                File.Move(oldFileTempName, fileName);
                originalRenamed = false;
            }
            catch { }
        }

        if (exception != null) MessageBox.Show(exception.Message);

        if (originalRenamed)
        {
            MessageBox.Show("'" + fileName + "'" +
                " have been corrupted or deleted in this operation.\n" +
                "A backup copy have been created at '" + oldFileTempName + "'");
        }
        else if (oldFileTempName != null) TryToDelete(oldFileTempName);
    }
}

Player.Math.RandomDigitsist nur eine Funktion, die eine Zeichenfolge mit zufälligen Ziffern zurückgibt. Denken Sie, dass mein Code sicher ist, wie ich denke, oder fehlt mir etwas? Es ist schwer für mich, jede mögliche Ausnahme zu testen.

Antworten
10

2 Antworten auf die Frage

5
Steven Jeuris

Die Verwendung von File.Replace () ist für mich immer noch die beste Option. Wie Sie zu SO erwähnt haben, könnte Folgendes ein Problem darstellen:

Wenn sich sourceFileName und DestinationFileName auf verschiedenen Volumes befinden, löst diese Methode eine Ausnahme aus. Wenn sich das destinationBackupFileName auf einem anderen Volume als der Quelldatei befindet, wird die Sicherungsdatei gelöscht.

Um zu verhindern, dass diese Ausnahme ausgelöst wird, können Sie nicht zuerst prüfen, ob Sie die temporäre Datei direkt auf das gewünschte Volume serialisieren können? Ist dies nicht möglich, schlägt die weitere Verarbeitung ebenfalls fehl, sodass Sie bereits eine Meldung anzeigen können, dass das Volume nicht beschreibbar ist.

Rufen Sie anschließend die ReplaceFunktion auf und erstellen Sie eine Sicherungsdatei. Wenn dies fehlschlägt, verhalten Sie sich entsprechend und prüfen Sie, in welchem ​​Status sich Ihre Dateien befinden. Geben Sie dem Benutzer an, wenn etwas "beschädigt" ist (zeigen Sie auf die Sicherungsdatei). Bei der finallyPrüfung ob die Sicherungsdatei existiert und entfernen.

Das klingt gut. Das einzige Problem ist der "Chech in welchem ​​Zustand sich Ihre Dateien befinden". Was passiert, wenn beim Überprüfen etwas fehlschlägt? `File.Exists` ist bekanntermaßen unzuverlässig. Mein Code weiß jedoch genau, in welchem ​​Zustand sich die Dateien befinden (das einzige, was nicht wirklich weiß, ist, ob die temporären Dateien gelöscht wurden oder nicht, aber das ist nicht so relevant wie das Wissen, ob die Originaldatei umbenannt wurde oder nicht). . Juan vor 8 Jahren 0
@jsoldi: Ich kenne keine Unzuverlässigkeiten von `File.Exists`. Steven Jeuris vor 8 Jahren 0
Ich habe es selbst gesehen. Manchmal gibt es false zurück, wenn die Datei existiert. Das hat mit der Virtualisierung von Dateien zu tun. Juan vor 8 Jahren 0
4
Brian Reichle
  • Ich mag es nicht, alle Ausnahmen von File.Delete () zu verwerfen. Ich denke, es ist in Ordnung, eine DirectoryNotFoundException hier zu verwerfen, aber ich denke, die anderen sollten sich weiter verbreiten dürfen und dem Benutzer angezeigt werden. Ich finde, dass die Tatsache, dass der Benutzer keinen Schreibzugriff hat oder dass die Datei in einem anderen Prozess gesperrt ist, im Allgemeinen dazu führt, dass später weitere verwirrende Fehler auftreten.

  • Sie serialisieren den neuen Inhalt innerhalb des großen Try-Catch. Wenn die Serialisierung fehlschlägt, teilen Sie dem Benutzer mit, dass die Datei beschädigt oder gelöscht wurde, und sie sollten von der bereitgestellten Sicherung wiederherstellen, obwohl Sie den Punkt, an dem die Sicherung erstellt wurde, noch nicht erreicht haben.

  • Die Idee, einen großen Anlauf zu haben und dann Flaggen zu verwenden, um festzustellen, in welchem ​​Stadium Sie sich befanden, macht mich etwas unruhig. Sie können das originalRenamedFlag durch ein Try-Catch-Symbol ersetzen, File.Move(tempNewFile, fileName)da dies der einzige Anruf ist, während das Flag auf true gesetzt ist.

    try
    {
        // ...
    
        try
        {
            File.Move(tempNewFile, fileName);
        }
        catch
        {
            if (!string.IsNullOrEmpty(oldFileTempName) && File.Exists(oldFileTempName))
            {
                File.Move(oldFileTempName, fileName);
            }
    
            MessageBox.Show("...");
    
            throw;
        }
    
        // ...
    
        if (!string.IsNullOrEmpty(oldFileTempName) && File.Exists(oldFileTempName))
        {
            TryToDelete(oldFileTempName);
        }
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    

    Dies kann unbequem sein, wenn Sie möchten, dass die Meldungsfelder in der bestimmten Reihenfolge angezeigt werden, aber ich denke nicht, dass dies ein zu großes Problem sein sollte.

Warten Sie, wenn die Serialisierung fehlschlägt, sage ich dem Benutzer nicht, dass die Datei beschädigt ist, da das `originalRenamed`-Flag nicht gesetzt wird. Der Grund, warum ich ein Flag verwende, anstatt die `File.Move (tempNewFile, fileName)` in einen Try-Catch zu setzen, ist, dass ich die beschädigte Datei nicht anzeigen möchte, wenn ich vorher `` nicht aufgerufen habe File.Move (Dateiname, oldFileTempName); `löschen Sie stattdessen einfach die TempNewFile und zeigen Sie die IOException. Macht das Sinn? Juan vor 8 Jahren 0
Aber ich stimme dem Ausschluss aller File.Delete-Ausnahmen zu. Juan vor 8 Jahren 0
Ja, Sie haben recht mit dem zweiten Punkt. Ich behaupte jedoch immer noch, dass es besser wäre, einen lokalisierten Try-Catch-Block zu haben, als Flags zu verwenden. In dem von mir bereitgestellten Beispiel wird geprüft, ob die Sicherung vorhanden ist, sodass die "beschädigte Nachricht" nicht angezeigt wird, wenn keine Sicherung erstellt wurde. Brian Reichle vor 8 Jahren 0