Search code examples
c#filesystemwatchermovefile

How to Monitor a Folder for files and move said files to another folder while running in background?


I want a program that will constantly monitor a folder for files, when a file appears in said folder the program should wait for the file to be accessible and then move said file to another folder. Currently the files are not being moved from folder "test" to "test2".

I made it so that when i click the start button the form is minimized and runs in background constantly monitoring the folder.

private void btstart_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;

            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = @"C:\test";
            watcher.NotifyFilter = NotifyFilters.LastWrite;



            watcher.Created += new FileSystemEventHandler(watcher_FileCreated);
            watcher.EnableRaisingEvents = true;
        }

        public static bool Ready(string filename)
        {
            try
            {
                using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
                    return inputStream.Length > 0;
            }
            catch (Exception)
            {
                return false;
            }
        }
        void watcher_FileCreated(object sender, FileSystemEventArgs e)
        {
            string path1 = @"C:\test";
            string path2 = @"C:\test2";
            string files = @"*.*"; 
            string[] fileList = Directory.GetFiles(path1, files);
            foreach (string file in fileList)
            {
                if (Ready(file) == true)
                {
                    File.Move(path1, path2);
                }
            }
        }

Apperently it is not obvious, but what is happening is that the file is not moved from folder "test" to folder "test2", there is no exception thrown, there are no errors, the file is not in use by anything nor is it open, permissions are all set correctly aswell, the file is simple not being moved

EDIT SOLUTION : The code works now thanks to the answer posted in this thread. Ive added a couple of things myself so that a duplicate exception will be handled.

folderlocationpath & folderdestinationpath variables are read through a folderbrowserdialog, so that user can choose the 2 folder locations himself This is what i currently have :

string path1 = folderlocationpath;
            string path2 = folderdestinationpath;
            string files = @"*.*";
            string[] fileList = Directory.GetFiles(path1, files);
            foreach (string file in fileList)
            {
                if (Ready(file) == true)
                    try
                    {
                        File.Move(file, Path.Combine(path2, Path.GetFileName(file)));
                    }
                    catch (IOException) // for duplicate files an exception that deletes the file in destination folder and then moves the file from origin folder
                    {
                        string files2 = Path.GetFileName(file);
                        string[] fileList2 = Directory.GetFiles(path2, files2);
                        foreach (string file2 in fileList2)
                            File.Delete(file2);

                        File.Move(file, Path.Combine(path2, Path.GetFileName(file)));
                    }
            }

Solution

  • I considered writing an edit to @BanMe answer, but it seems like it would be better to be a bit more all-inclusive in the response, as I would effectively be adding an additional change to his suggested fix (which, in reality, is quite important) after testing on my own.

    This has been tested, and is verified to work on my system. There was one additional changes that I had to make to get this to work more effectively. It incorporates the fix to the Move command, Path.Combine but more importantly adds a NotifyFilter to FileName as well.

    You should be able to emplace this section of your code, and it should work as expected from my testing.

    private void btstart_Click(object sender, EventArgs e)
    {
        this.WindowState = FormWindowState.Minimized;
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = @"C:\test";
        watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
        watcher.Created += new FileSystemEventHandler(watcher_FileCreated);
        watcher.EnableRaisingEvents = true;
    }
    
    public static bool Ready(string filename)
    {
        try
        {
            using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
                return inputStream.Length > 0;
        }
        catch (Exception)
        {
            return false;
        }
    }
    
    void watcher_FileCreated(object sender, FileSystemEventArgs e)
    {
        string path1 = @"C:\test";
        string path2 = @"C:\test2";
        string files = @"*.*";
        string[] fileList = Directory.GetFiles(path1, files);
        foreach (string file in fileList)
        {
            if (Ready(file) == true)
            {
                File.Move(file, Path.Combine(path2, System.IO.Path.GetFileName(file)));
            }
        }
    }
    

    Why do we need to expand the NotifyFilters? Take a look at this answer which I'll sum up the relevant portion here:

    I've been having trouble with this behavior too. If you step through the code (and if you look at MSDN documenation, you'll find that NotifyFilter starts off with a default value of:

    NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite

    So when you say .NotifyFilter = NotifyFilters.CreationTime, you're wiping out those other values, which explains the difference in behavior. I'm not sure why NotifyFilters.CreationTime is not catching the new file... seems like it should, shouldn't it!

    It appears that your isolating the NotifyFilter to just LastWrite is ignoring other aspects of the FileSystemWatcher. Adding in FileName resolved the issue for me.

    You may still hit an exception if the files exist in the C:\TEST2\ folder already, but that's a very simple adjustment to make prior to the File.Move if you have to.