Warten Sie, bis die Datei vollständig geschrieben ist

77

Wenn eine Datei FileSystemWatcher_Createdin einem Verzeichnis erstellt ( ) wird, kopiere ich sie in ein anderes. Aber wenn ich ein großes (> 10 MB) Datei erstellen kann es die Datei kopieren, weil es das Kopieren beginnt bereits, wenn die Datei noch nicht fertig zu schaffen , ist ...
Diese Ursachen können nicht die Datei zu kopieren, weil es von einem anderen Prozess verwendet wird zu sein angehoben. ; (
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();
    }
}
Levi
quelle
1
Ich Antwort hier leider gefunden [Dieses Problem gelöst auf Stackoverflow] [1] [1]: stackoverflow.com/questions/1406808/...
levi

Antworten:

44

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 mit dem Kopieren beginnen. Sie können die folgende Funktion aufrufen, bis Sie den Wert False erhalten.

1. Methode, direkt aus dieser Antwort kopiert :

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;
}
Romil Kumar Jain
quelle
2
Und Sie haben vergessen, die konstanten Werte ( ERROR_SHARING_VIOLATION= 32, ERROR_LOCK_VIOLATION= 33) zu kopieren
Julien N
@RomilKumarJain Können wir die Verwendung dieser Methode sehen?
Roxy'Pro
@ Roxy'Pro Was meinst du mit Nutzung?
Romil Kumar Jain
14

Aus der Dokumentation für FileSystemWatcher:

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

Wenn die Kopie fehlschlägt (Ausnahme abfangen), fügen Sie sie einer Liste von Dateien hinzu, die noch verschoben werden müssen, und versuchen Sie die Kopie während des OnChangedEreignisses. Schließlich sollte es funktionieren.

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 {}
     }

}}

Steve Czetty
quelle
Das ist ziemlich gut, aber ich brauche es sofort, also habe ich das IsFileReady in eine Weile gesteckt und es funktioniert jetzt
Levi
11

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

Ich habe ein ähnliches Problem mit einem Programm festgestellt, das PDF-Dateien schreibt. Manchmal dauert das Rendern 30 Sekunden. Dies ist derselbe Zeitraum, den meine watcher_FileCreated-Klasse vor dem Kopieren der Datei wartet.

Die Dateien wurden nicht gesperrt.

In diesem Fall habe ich die Größe der PDF-Datei überprüft und dann 2 Sekunden gewartet, bevor ich die neue Größe verglichen habe. Wenn sie ungleich wären, würde der Thread 30 Sekunden lang schlafen und es erneut versuchen.

Johnny Grimes
quelle
5

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 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 verwendet wird (noch besser - versuchen Sie, sie im exklusiven Modus zu öffnen, und prüfen Sie, ob sie gerade von einer anderen Person geöffnet wird, anstatt den Fehler von File.Copy zu erraten). 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 Rennbedingung).

Wann ist das "andere Mal"? 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 Uhrzeit einrichten und die Datei in 5 Sekunden erneut überprüfen.

zmbq
quelle
3

Nun, Sie geben die Antwort bereits selbst; Sie müssen warten, bis die Erstellung der Datei abgeschlossen ist. Eine Möglichkeit, dies zu tun, 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 so etwas wie (Pseudocode) haben:

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

   CopyFile()
}

Natürlich sollten Sie sich vor einer Unendlichkeit schützen, whilenur für den Fall, dass die Eigentümeranwendung die Sperre niemals aufhebt. Es könnte sich auch lohnen, sich die anderen Ereignisse anzusehen, die FileSystemWatcherSie abonnieren können. Möglicherweise gibt es ein Ereignis, mit dem Sie das gesamte Problem umgehen können.

Pyrocumulus
quelle
2

Wenn die Datei binär geschrieben wird (Byte für Byte), erstellen Sie FileStream und höhere Lösungen. Funktioniert nicht. Da die Datei in allen Bytes bereit und fehlerhaft ist, benötigen Sie in dieser Situation eine andere Problemumgehung wie diese: Führen Sie dies aus, wenn die Datei erstellt wurde oder Sie möchten um die Verarbeitung in 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!
Mohsen.Sharify
quelle
2

Nachdem ich einige dieser und ähnliche Fragen schnell durchgesehen hatte, machte ich heute Nachmittag eine lustige Gänsejagd und versuchte, ein Problem mit zwei separaten Programmen zu lösen, wobei eine Datei als Synchronisationsmethode (und auch zum Speichern von Dateien) verwendet wurde. Eine etwas ungewöhnliche Situation, aber sie hat für mich definitiv die Probleme mit dem Ansatz "Überprüfen, ob die Datei gesperrt ist, öffnen Sie sie, wenn dies nicht der Fall ist" hervorgehoben.

Das Problem ist folgendes: Die Datei kann zwischen dem Zeitpunkt, zu dem Sie sie überprüfen, und dem Zeitpunkt, zu dem Sie die Datei tatsächlich öffnen , gesperrt werden. Es ist wirklich schwierig, das Sporadische aufzuspüren. Die Datei kann nicht kopiert werden, da sie von einem anderen Prozessfehler verwendet wird, 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 vergeht zwischen der Prüfung und dem Öffnen keine Zeit, das Betriebssystem führt sie gleichzeitig aus.

Der Code hier verwendet File.Copy, funktioniert jedoch 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 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, wie man es parellelisieren kann.

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!
ashbygeek
quelle
0

Mit dem folgenden Code können Sie überprüfen, ob die Datei mit exklusivem Zugriff geöffnet werden kann (dh, sie wird nicht von einer anderen Anwendung geöffnet). Wenn die Datei nicht geschlossen ist, können Sie einige Momente 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 des Überprüfens der Datei und dem Zeitpunkt des Kopierens ö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;
    }
}
Michael
quelle
Was gibt dieser Code zurück, wenn Sie auf eine Ausnahme stoßen, die keine IOException ist?
William Daniel
Wenn File.Open eine andere Ausnahme als IOException auslöst, gibt diese Methode keinen Wert zurück. Es liegt in der Verantwortung des Anrufers, nach Nicht-IO-Ausnahmen zu suchen, die möglicherweise ausgelöst werden: msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
Michael
-5

Ich möchte hier eine Antwort hinzufügen, da dies für mich funktioniert hat. Ich benutzte Zeitverzögerungen, während Schleifen, alles, was mir einfiel.

Ich hatte das Windows Explorer-Fenster des Ausgabeordners geöffnet. Ich schloss es und alles funktionierte wie ein Zauber.

Ich hoffe das hilft jemandem.

Firkamon
quelle
4
Jetzt stürzt Ihre App ab, wenn der Benutzer dieses Verzeichnis geöffnet hat.
Epirocks