简体   繁体   中英

How to determine whether a folder has finished copying c#

I have a question: How do I determine whether a folder has finished copying from one location to another?

At the moment my FileSystemWatcher triggers several events as soon as a file within the directory being copied. What I want though, is one single event to be triggered when all the files within that folder has been successfully copied. My code right now looks like this:

static void Main(string[] args)
    {
        String path = @"D:\Music";
        FileSystemWatcher mWatcher = new FileSystemWatcher();
        mWatcher.Path = path;
        mWatcher.NotifyFilter = NotifyFilters.LastAccess;
        mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
        mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
        mWatcher.IncludeSubdirectories = true;
        mWatcher.Created += new FileSystemEventHandler(mLastChange);
        mWatcher.Changed += new FileSystemEventHandler(mLastChange);

        mWatcher.EnableRaisingEvents = true;
        Console.WriteLine("Watching path: " + path);



        String exit;
        while (true)
        {
               exit = Console.ReadLine();
               if (exit == "exit")
                   break;

        }


    }



    private static void mLastChange(Object sender, FileSystemEventArgs e)
    {
        Console.WriteLine(e.ChangeType + " " + e.FullPath);
    }

Unfortunately FileSystemWatcher doesn't tell you when a file is finished writing. So your options are...

  1. Set a timeout after last write when it is assumed there are no more changes coming
  2. Have the writing application put a lock file of some variety that tells any other program that it's done.

After re-reading your question... it doesn't sound like you have any control over the other application.

So you will need some kind of timeout value that determines when all the writing is done. Basically create a timer that resets after each filesystemwatcher event... when it times out then you fire the single event that says it's done.

Here is how you could add it to your code...

static void Main(string[] args)
{
    Timer.Interval = 5000; // 5 seconds - change to whatever is appropriate
    Timer.AutoReset = false;
    Timer.Elapsed += TimeoutDone;
    String path = @"D:\Music";
    FileSystemWatcher mWatcher = new FileSystemWatcher();
    mWatcher.Path = path;
    mWatcher.NotifyFilter = NotifyFilters.LastAccess;
    mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.LastWrite;
    mWatcher.NotifyFilter = mWatcher.NotifyFilter | NotifyFilters.DirectoryName;
    mWatcher.IncludeSubdirectories = true;
    mWatcher.Created += new FileSystemEventHandler(mLastChange);
    mWatcher.Changed += new FileSystemEventHandler(mLastChange);

    mWatcher.EnableRaisingEvents = true;
    Console.WriteLine("Watching path: " + path);
    Timer.Start();

    String exit;
    while (true)
    {
        exit = Console.ReadLine();
        if (exit == "exit")
            break;

    }
}

private static Timer Timer = new Timer();

private static void TimeoutDone(object source, ElapsedEventArgs e)
{
    Console.WriteLine("Timer elapsed!");
}

private static void mLastChange(Object sender, FileSystemEventArgs e)
{
    Console.WriteLine(e.ChangeType + " " + e.FullPath);
    if (Timer != null)
    {
        Timer.Stop();
        Timer.Start();
    }
}

It's horribly cheesy, but in the past I have dealt with this problem by creating a custom decorator for the FileSystemWatcher class. Internally it creates a FileSystemWatcher and registers for the Created and Changed events, wraps them, and throws its own Created and Changed events after the files are finished, similar to this:

private void Watcher_Changed(Object sender, FileSystemEVentArgs e)
    {
        while (true)
        {
            FileStream stream;
            try
            {
                stream = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                // If this succeeds, the file is finished
                Changed();
            }
            catch (IOException)
            {
            }
            finally
            {
                if (stream != null) stream.Close();
            }
        }
    }

Some of this is drawn from the answer here . In reality, you shouldn't use an infinite loop. You probably want to add timeouts, sleep in between checks, etc. but this is the general idea.

If the destination is a local folder, you can use the filesystem filter driver to track file create and file close events. Knowing when all files, previously created, are closed will inform you that copying is complete.

I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done.

Download FileSystemSafeWatcher and add it to your project.

Then use it as a normal FileSystemWatcher and monitor when the events are triggered.

var fsw = new FileExamSystemWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;

Unfortunatly there is no ready solution. but I designed a simple tricky solution to trigger (copy finished) event.

you should use timer.

        FileSystemWatcher fsw = new FileSystemWatcher();
        string fullPath = "";
        DateTime tempTime;
        fsw.Path = @"C:\temp";

        private void startwatching()
        {
            timer1.Start();
        }
        fsw.EnableRaisingEvents = true;
        fsw.Created += Fsw_Created;

        private void Fsw_Created(object sender, FileSystemEventArgs e)
        {
            tempTime = DateTime.Now.AddSeconds(-4);
            fullPath = e.FullPath;
        }

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (fullPath!=string.Empty)
        {
            timer1.Stop();
            if (tempTime >= Directory.GetLastAccessTime(fullPath))
            {
                DirectoryInfo di = new DirectoryInfo(fullPath);
                listBox1.Items.Add("Folder " + di.Name + " finished copying");
                fullPath = string.Empty;
            }
            else
            {
                tempTime = DateTime.Now;
            }
            timer1.Start();
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM