Attendre Jusqu'À Ce Que Le Fichier Soit Complètement Écrit

Lorsqu'un fichier est créé ( FileSystemWatcher_Created ) dans un répertoire, je le copie à un autre. Mais quand je crée un gros fichier (>10MB) il ne copie pas le fichier, parce qu'il commence déjà à copier, quand le fichier n'est pas encore terminé la création...

Cela fait que ne peut pas copier le fichier, car il est utilisé par un autre processus pour être soulevé. ; (

Toute aide?

class Program
{
    static void Main(string[] args)
    {
        string path = @"D:levanFolderListenerTestListenedFolder";
        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:levanFolderListenerTestCopiedFilesFolder" + e.Name);
        Console.Read();
    }
}
51
demandé sur levi 2012-06-11 18:29:57

9 réponses

il n'y a que des solutions de rechange pour le problème auquel vous faites face.

vérifiez si l'id du fichier est en cours de traitement avant de commencer le processus de copie. Vous pouvez appeler la fonction suivante jusqu'à ce que vous obtenez la valeur False.

1ère Méthode, copiés directement à partir de cette réponse :

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

deuxième méthode:

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;
}
36
répondu Romil Kumar Jain 2017-11-01 07:53:49

de la documentation pour FileSystemWatcher :

l'événement OnCreated est soulevé dès qu'un fichier est créé. Si un fichier est copié ou transféré dans un répertoire surveillé, le L'événement OnCreated sera soulevé immédiatement, suivi d'un ou plusieurs OnChanged des événements.

Donc, si la copie échoue, (intercepter l'exception), l'ajouter à une liste de fichiers qui doivent encore être déplacé, et à essayer de copier lors de l'événement OnChanged . Finalement, il devrait fonctionner.

quelque Chose comme (incomplet; attraper les exceptions spécifiques, initialiser des variables, etc):

    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 {}
         }
   }
7
répondu Steve Czetty 2012-06-11 14:41:36

c'est un vieux fil, mais je vais ajouter quelques informations pour d'autres personnes.

j'ai connu un problème similaire avec un programme qui écrit des fichiers PDF, parfois ils prennent 30 secondes à rendre.. c'est la même période que ma classe watcher_FileCreated attend avant de copier le fichier.

les dossiers n'étaient pas verrouillés.

dans ce cas, j'ai vérifié la taille du PDF et attendu 2 secondes avant de comparer la nouvelle taille, si elles étaient l'inégalité de la thread en veille pendant 30 secondes et essayez à nouveau.

6
répondu Johnny Grimes 2015-05-20 02:24:56

vous êtes en fait dans la chance - le programme écrivant le fichier le verrouille, donc vous ne pouvez pas l'ouvrir. S'il ne l'avait pas verrouillé, vous auriez copié un fichier partiel, sans avoir la moindre idée qu'il y a un problème.

quand vous ne pouvez pas accéder à un fichier, vous pouvez supposer qu'il est encore utilisé (mieux encore - essayer de l'ouvrir en mode exclusif, et voir si quelqu'un d'autre est en train de l'ouvrir, au lieu de deviner de L'échec de fichier.Copie.) Si le fichier est verrouillé, vous devrez le copier à un certain d'autres temps. Si elle n'est pas verrouillée, vous pouvez la copier (Il y a un léger potentiel pour une condition de course ici).

C'est quand "l'autre fois"? Je ne me souviens pas quand FileSystemWatcher envoie plusieurs événements par fichier - vérifiez-le, Il pourrait être suffisant pour vous d'ignorer l'événement et d'attendre un autre. Sinon, vous pouvez toujours configurer une heure et revérifier le fichier en 5 secondes.

4
répondu zmbq 2012-06-11 14:35:20

Eh bien, vous donnez déjà la réponse vous-même; vous devez attendre la création du fichier pour terminer. Une façon de le faire est de vérifier si le fichier est toujours utilisé. Un exemple de ceci peut être trouvé ici: Est-il un moyen de vérifier si un fichier est en cours d'utilisation?

Notez que vous devrez modifier ce code pour qu'il fonctionne dans votre situation. Vous pourriez vouloir avoir quelque chose comme (pseudocode):

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

   CopyFile()
}

évidemment vous devez vous protéger d'un infini while juste au cas où l'application propriétaire ne libère jamais la serrure. Aussi, il pourrait être utile de vérifier les autres événements de FileSystemWatcher vous pouvez vous abonner. Il pourrait y avoir un événement que vous pouvez utiliser pour contourner tout ce problème.

2
répondu pyrocumulus 2017-05-23 12:18:17

donc, après avoir jeté un coup d'oeil rapide à certaines de ces questions et d'autres similaires, je suis allé sur une chasse d'oie merry cet après-midi en essayant de résoudre un problème avec deux programmes séparés en utilisant un fichier comme une méthode de synchronisation (et aussi enregistrer fichier). Un peu d'une situation inhabituelle, mais il a certainement mis en évidence pour moi les problèmes avec le "vérifier si le fichier est verrouillé, puis l'ouvrir si ce n'est pas" approche.

le problème est celui-ci: le fichier peut devenir verrouillé entre le moment où vous le vérifiez et le moment où vous ouvrez réellement le fichier. Il est vraiment difficile de trouver le sporadique ne peut pas copier le fichier, parce qu'il est utilisé par un autre processus erreur si vous n'êtes pas à la recherche trop.

la résolution de base est de simplement essayer d'ouvrir le fichier à l'intérieur d'un bloc catch de sorte que si son verrouillé, vous pouvez essayer à nouveau. De cette façon, il n'y a pas de temps écoulé entre la vérification et l'ouverture, l'OS ne même temps.

le code utilise ici le fichier.Copiez, mais cela fonctionne aussi bien avec n'importe laquelle des méthodes statiques de la classe File: File.Ouvrir, Fichier.ReadAllText, File.WriteAllText, etc.

/// <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;
    }
}

une autre petite note sur le parellisme: C'est une méthode synchrone, qui bloque son thread à la fois en attendant et en travaillant sur le thread. C'est l'approche la plus simple, mais si le fichier reste verrouillé pendant longtemps votre le programme peut devenir insensible. Le parellélisme est un sujet trop vaste pour entrer en profondeur ici (et le nombre de façons dont vous pourriez configurer la lecture/écriture asynchrone est un peu absurde), mais voici une façon dont il pourrait être parellisé.

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

et voici comment il peut être utilisé:

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!
1
répondu ashbygeek 2017-01-06 02:26:21

, Vous pouvez utiliser le code suivant pour vérifier si le fichier peut être ouvert avec un accès exclusif (qui est, il n'est pas ouvert par une autre application). Si le fichier n'est pas fermé, on pourrait attendre quelques instants et vérifier de nouveau jusqu'à ce que le fichier est fermé et vous pouvez le copier.

Vous devriez toujours vérifier si le Fichier.Copie échoue, parce qu'une autre application peut ouvrir le fichier entre le moment où vous vérifiez le fichier et le moment de la copier.

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
répondu Michael 2012-06-11 14:43:10

lorsque le fichier est écrit en binaire (byte Byte), créer FileStream et les solutions ci-dessus ne fonctionne pas, parce que le fichier est prêt et wrotted dans chaque Byte,donc dans cette Situation, vous avez besoin d'autres contournements comme celui-ci: Faites ceci lorsque le fichier créé ou vous voulez commencer le traitement sur le fichier

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!
0
répondu Mohsen.Sharify 2016-11-15 13:24:43

je voudrais ajouter une réponse ici, parce que cela a fonctionné pour moi. J'ai utilisé des délais, des boucles, tout ce à quoi je pouvais penser.

j'avais la fenêtre Windows Explorer du dossier de sortie ouverte. Je l'ai fermé, et tout a fonctionné comme un charme.

j'espère que cela aidera quelqu'un.

-3
répondu Firkamon 2015-07-30 12:47:02