C# FileSystemWatcher, How to know file copied completely into the watch folder

后端 未结 7 1215
予麋鹿
予麋鹿 2020-12-06 20:14

I am developing a .net application, where I am using FileSystemWatcher class and attached its Created event on a folder. I have to do action on this event (i.e. copy file to

7条回答
  •  天涯浪人
    2020-12-06 20:42

    Three issues with FileSystemWatcher, the first is that it can send out duplicate creation events so you check for that with something like:

    this.watcher.Created += (s, e) =>
    {
        if (!this.seen.ContainsKey(e.FullPath) 
            || (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
        {
            this.seen[e.FullPath] = DateTime.Now;
            ThreadPool.QueueUserWorkItem(
                this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
        }
    };
    

    where this.seen is a Dictionary and this.seenInterval is a TimeSpan.

    Next, you have to wait around for the file creator to finish writing it (the issue raised in the question). And, third, you must be careful because sometimes the file creation event gets thrown before the file can be opened without giving you a FileNotFoundException but it can also be removed before you can get a hold of it which also gives a FileNotFoundException.

    private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
    {
        // Make sure the just-found file is done being
        // written by repeatedly attempting to open it
        // for exclusive access.
        var path = (string)threadContext;
        DateTime started = DateTime.Now;
        DateTime lastLengthChange = DateTime.Now;
        long lastLength = 0;
        var noGrowthLimit = new TimeSpan(0, 5, 0);
        var notFoundLimit = new TimeSpan(0, 0, 1);
    
        for (int tries = 0;; ++tries)
        {
            try
            {
                using (var fileStream = new FileStream(
                   path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    // Do Stuff
                }
    
                break;
            }
            catch (FileNotFoundException)
            {
                // Sometimes the file appears before it is there.
                if (DateTime.Now - started > notFoundLimit)
                {
                    // Should be there by now
                    break;
                }
            }
            catch (IOException ex)
            {
                // mask in severity, customer, and code
                var hr = (int)(ex.HResult & 0xA000FFFF);
                if (hr != 0x80000020 && hr != 0x80000021)
                {
                    // not a share violation or a lock violation
                    throw;
                }
            }
    
            try
            {
                var fi = new FileInfo(path);
                if (fi.Length > lastLength)
                {
                    lastLength = fi.Length;
                    lastLengthChange = DateTime.Now;
                }
            }
            catch (Exception ex)
            {
            }
    
            // still locked
            if (DateTime.Now - lastLengthChange > noGrowthLimit)
            {
                // 5 minutes, still locked, no growth.
                break;
            }
    
            Thread.Sleep(111);
        }
    

    You can, of course, set your own timeouts. This code leaves enough time for a 5 minute hang. Real code would also have a flag to exit the thread if requested.

提交回复
热议问题