it-swarm.com.de

Warten Sie, bis die Datei vollständig geschrieben wurde

Wenn eine Datei erstellt wird (FileSystemWatcher_Created) In ein Verzeichnis kopiere ich es in ein anderes. Aber wenn ich eine große (> 10 MB) Datei erstelle, kann sie nicht kopiert werden, da der Kopiervorgang bereits beginnt, wenn die Datei noch nicht fertig ist.
Dies führt dazu, dass Die Datei kann nicht kopiert werden, da sie von einem anderen Prozess verwendet wird ausgelöst wird. ; (
Irgendeine Hilfe?

class Program
{
    static void Main(string[] args)
    {
        string path = @"D:\levan\FolderListenerTest\ListenedFolder";
        FileSystemWatcher listener; 
        listener = new FileSystemWatcher(path);
        listener.Created += new FileSystemEventHandler(listener_Created);
        listener.EnableRaisingEvents = true;

        while (Console.ReadLine() != "exit") ;
    }

    public static void listener_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine
                (
                    "File Created:\n"
                   + "ChangeType: " + e.ChangeType
                   + "\nName: " + e.Name
                   + "\nFullPath: " + e.FullPath
                );
        File.Copy(e.FullPath, @"D:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name);
        Console.Read();
    }
}
63
levi

Es gibt nur eine Problemumgehung für das Problem, mit dem Sie konfrontiert sind.

Überprüfen Sie, ob die Datei-ID in Bearbeitung ist, bevor Sie den Kopiervorgang starten. Sie können die folgende Funktion aufrufen, bis Sie den Wert False erhalten.

1. Methode, direkt aus diese Antwort :

private bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

2. Methode:

const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private bool IsFileLocked(string file)
{
    //check that problem is not in destination file
    if (File.Exists(file) == true)
    {
        FileStream stream = null;
        try
        {
            stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (Exception ex2)
        {
            //_log.WriteLog(ex2, "Error in checking whether file is locked " + file);
            int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1);
            if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION))
            {
                return true;
            }
        }
        finally
        {
            if (stream != null)
                stream.Close();
        }
    }
    return false;
}
40

Aus der Dokumentation für FileSystemWatcher:

Das Ereignis OnCreated wird ausgelöst, sobald eine Datei erstellt wird. Wenn eine Datei kopiert oder in ein überwachtes Verzeichnis übertragen wird, wird das Ereignis OnCreated sofort ausgelöst, gefolgt von einem oder mehreren Ereignissen OnChanged.

Wenn die Kopie fehlschlägt (Ausnahme abfangen), fügen Sie sie zu einer Liste von Dateien hinzu, die noch verschoben werden müssen, und versuchen Sie die Kopie während des Ereignisses OnChanged. Irgendwann sollte es klappen.

So etwas wie (unvollständig; bestimmte Ausnahmen abfangen, Variablen initialisieren usw.):

    public static void listener_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine
                (
                    "File Created:\n"
                   + "ChangeType: " + e.ChangeType
                   + "\nName: " + e.Name
                   + "\nFullPath: " + e.FullPath
                );
        try {
            File.Copy(e.FullPath, @"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name);
        }
        catch {
            _waitingForClose.Add(e.FullPath);
        }
        Console.Read();
    }

    public static void listener_Changed(object sender, FileSystemEventArgs e)
    {
         if (_waitingForClose.Contains(e.FullPath))
         {
              try {
                  File.Copy(...);
                  _waitingForClose.Remove(e.FullPath);
              }
              catch {}
         }
   }
10
Steve Czetty

Es ist ein alter Thread, aber ich werde ein paar Informationen für andere Leute hinzufügen.

Bei einem Programm, das PDF Dateien schreibt, trat ein ähnliches Problem auf. Manchmal dauert das Rendern 30 Sekunden. Dies entspricht dem Zeitraum, den meine watcher_FileCreated-Klasse vor dem Kopieren der Datei wartet.

Die Dateien wurden nicht gesperrt.

In diesem Fall überprüfte ich die Größe von PDF und wartete dann 2 Sekunden, bevor ich die neue Größe verglich. Wenn sie ungleich waren, schlief der Thread 30 Sekunden lang und versuchte es dann erneut.

10
Johnny Grimes

Sie haben tatsächlich Glück - das Programm, das die Datei schreibt, sperrt sie, sodass Sie sie nicht öffnen können. Wenn es nicht gesperrt worden wäre, hätten Sie eine Teildatei kopiert, ohne zu ahnen, dass ein Problem vorliegt.

Wenn Sie nicht auf eine Datei zugreifen können, können Sie davon ausgehen, dass sie noch in Gebrauch ist (besser noch - versuchen Sie, sie im exklusiven Modus zu öffnen, und prüfen Sie, ob sie gerade von einer anderen Person geöffnet wird, anstatt zu erraten, ob File.Copy fehlgeschlagen ist). Wenn die Datei gesperrt ist, müssen Sie sie zu einem anderen Zeitpunkt kopieren. Wenn es nicht gesperrt ist, können Sie es kopieren (hier besteht ein geringes Potenzial für eine Race Condition).

Wann ist diese "andere Zeit"? Ich erinnere mich nicht, wenn FileSystemWatcher mehrere Ereignisse pro Datei sendet. Probieren Sie es aus, es könnte ausreichen, wenn Sie das Ereignis einfach ignorieren und auf ein anderes warten. Wenn nicht, können Sie jederzeit eine Zeit festlegen und die Datei in 5 Sekunden erneut überprüfen.

5
zmbq

Nun, Sie geben die Antwort bereits selbst; Sie müssen warten, bis die Erstellung der Datei abgeschlossen ist. Eine Möglichkeit besteht darin, zu überprüfen, ob die Datei noch verwendet wird. Ein Beispiel hierfür finden Sie hier: Gibt es eine Möglichkeit, zu überprüfen, ob eine Datei verwendet wird?

Beachten Sie, dass Sie diesen Code ändern müssen, damit er in Ihrer Situation funktioniert. Vielleicht möchten Sie etwas wie (Pseudocode):

public static void listener_Created()
{
   while CheckFileInUse()
      wait 1000 milliseconds

   CopyFile()
}

Natürlich sollten Sie sich vor einem unendlichen while schützen, nur für den Fall, dass die Eigentümeranwendung die Sperre niemals aufhebt. Es könnte sich auch lohnen, die anderen Ereignisse in FileSystemWatcher zu prüfen, die Sie abonnieren können. Möglicherweise gibt es ein Ereignis, mit dem Sie das gesamte Problem umgehen können.

2
pyrocumulus

Nachdem ich einige dieser und ähnliche Fragen schnell durchgesehen hatte, machte ich heute Nachmittag eine lustige Verfolgungsjagd und versuchte, ein Problem mit zwei separaten Programmen zu lösen, indem ich eine Datei als Synchronisationsmethode (und auch als Methode zum Speichern von Dateien) verwendete. Eine etwas ungewöhnliche Situation, aber sie hat mir definitiv die Probleme aufgezeigt, die mit der Methode "Überprüfen, ob die Datei gesperrt ist, und öffnen, wenn dies nicht der Fall ist" verbunden sind.

Das Problem ist: Die Datei kann werden zwischen dem Zeitpunkt, zu dem Sie sie überprüfen, und dem Zeitpunkt, zu dem Sie die Datei tatsächlich öffnen, gesperrt sein. Es ist wirklich schwer, die sporadische Kann die Datei nicht kopieren, da sie von einem anderen Prozess verwendet wird Fehler, wenn Sie nicht auch danach suchen.

Die grundlegende Lösung besteht darin, nur zu versuchen, die Datei in einem catch-Block zu öffnen, damit Sie es erneut versuchen können, wenn sie gesperrt ist. Auf diese Weise gibt es keine verstrichene Zeit zwischen der Prüfung und dem Öffnen, das Betriebssystem führt sie gleichzeitig aus.

Der Code hier verwendet File.Copy, funktioniert aber genauso gut mit allen statischen Methoden der File-Klasse: File.Open, File.ReadAllText, File.WriteAllText usw.

/// <param name="timeout">how long to keep trying in milliseconds</param>
static void safeCopy(string src, string dst, int timeout)
{
    while (timeout > 0)
    {
        try
        {
            File.Copy(src, dst);

            //don't forget to either return from the function or break out fo the while loop
            break;
        }
        catch (IOException)
        {
            //you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible
        }
        Thread.Sleep(100);

        //if its a very long wait this will acumulate very small errors. 
        //For most things it's probably fine, but if you need precision over a long time span, consider
        //   using some sort of timer or DateTime.Now as a better alternative
        timeout -= 100;
    }
}

Noch ein kleiner Hinweis zum Parellelismus: Dies ist eine synchrone Methode, die ihren Thread sowohl während des Wartens als auch während der Arbeit am Thread blockiert. Dies ist der einfachste Ansatz. Wenn die Datei jedoch für längere Zeit gesperrt bleibt, reagiert Ihr Programm möglicherweise nicht mehr. Parellelismus ist ein zu großes Thema, um hier näher darauf einzugehen (und die Anzahl der Möglichkeiten, wie Sie asynchrones Lesen/Schreiben einrichten können, ist absurd), aber hier ist eine Möglichkeit, es zu parellelisieren.

public class FileEx
{
    public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone)
    {
        while (timeout > 0)
        {
            try
            {
                File.Copy(src, dst);
                doWhenDone();
                break;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }

    public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout)
    {
        while (timeout > 0)
        {
            try {
                return File.ReadAllText(filePath);
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
        return "";
    }

    public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout)
    {
        while (timeout > 0)
        {
            try
            {
                File.WriteAllText(filePath, contents);
                return;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }
}

Und so könnte es verwendet werden:

public static void Main()
{
    test_FileEx();
    Console.WriteLine("Me First!");
}    

public static async void test_FileEx()
{
    await Task.Delay(1);

    //you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy
    //As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously
    //until the first 'await'. See the documentation for async: https://msdn.Microsoft.com/en-us/library/hh156513.aspx
    CopyWaitAsync("file1.txt", "file1.bat", 1000);

    //this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes
    await CopyWaitAsync("file1.txt", "file1.readme", 1000);
    Console.WriteLine("file1.txt copied to file1.readme");

    //The following line doesn't cause a compiler error, but it doesn't make any sense either.
    ReadAllTextWaitAsync("file1.readme", 1000);

    //To get the return value of the function, you have to use this function with the await keyword
    string text = await ReadAllTextWaitAsync("file1.readme", 1000);
    Console.WriteLine("file1.readme says: " + text);
}

//Output:
//Me First!
//file1.txt copied to file1.readme
//file1.readme says: Text to be duplicated!
2
ashbygeek

Wenn die Datei binär (byteweise) geschrieben wird, erstellen Sie FileStream und höher. Lösungen Funktionieren nicht, da die Datei in allen Bytes bereit und geschrieben ist. In dieser Situation ist eine andere Problemumgehung erforderlich: Führen Sie diese Schritte aus, wenn Sie eine Datei erstellt haben oder möchten um die Bearbeitung der Datei zu starten

long fileSize = 0;
currentFile = new FileInfo(path);

while (fileSize < currentFile.Length)//check size is stable or increased
{
  fileSize = currentFile.Length;//get current size
  System.Threading.Thread.Sleep(500);//wait a moment for processing copy
  currentFile.Refresh();//refresh length value
}

//Now file is ready for any process!
1
Mohsen.Sharify

Mit dem folgenden Code können Sie überprüfen, ob die Datei mit exklusivem Zugriff geöffnet werden kann (dh nicht von einer anderen Anwendung geöffnet wird). Wenn die Datei nicht geschlossen ist, können Sie einen Moment warten und erneut prüfen, bis die Datei geschlossen ist und Sie sie sicher kopieren können.

Sie sollten weiterhin prüfen, ob File.Copy fehlschlägt, da eine andere Anwendung die Datei möglicherweise zwischen dem Zeitpunkt, zu dem Sie die Datei überprüfen, und dem Zeitpunkt, zu dem Sie sie kopieren, öffnet.

public static bool IsFileClosed(string filename)
{
    try
    {
        using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            return true;
        }
    }
    catch (IOException)
    {
        return false;
    }
}
0
Michael