1

I'm monitoring a Folder for File creation(Copied) event using FileSystem Watcher. I only want the program to process image Files.

FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(watcher_FileCreated);
watcher.Path = path;

So I try to create a Bitmap and avoid the file if an exception is thrown

private static void watcher_FileCreated(object sender, FileSystemEventArgs e)
{
    try
    {
        using (Bitmap test = new Bitmap(Bitmap.FromFile(e.FullPath)))
        {
            mytoprocesslist.add(e.FullPath);
        }

        //do my processing with image 
        Console.WriteLine(e.FullPath);
    }
    catch (Exception error)
    {
       Console.WriteLine("File Error");
    }
 }

This throws Out of Memory exception even when a valid image file is copied, which I think happens because the event was raised before the file was copied completely. How can I get over this? I only want to add the valid image files to a to do list and I will process these images one by one later.

mason
  • 31,774
  • 10
  • 77
  • 121
techno
  • 6,100
  • 16
  • 86
  • 192
  • above code does not seem to have any line which might throw out of memory. Most probably your "//do my processing with image" is throwing this error. You can add wait of 1 sec if you feel the file is not yet copied completely. – puneet Jul 21 '16 at 04:28
  • @puneet `using (Bitmap test = new Bitmap(Bitmap.FromFile(e.FullPath)))` – techno Jul 21 '16 at 04:30
  • I set `watcher.NotifyFilter = NotifyFilters.FileName;` and to load image, I'm using `Image.FromFile(e.FullPath);` and it works, without any exception. –  Jul 21 '16 at 05:29
  • To the person who has downvoted this,it would be good to leave a comment. – techno Jul 21 '16 at 05:34
  • @x... what does setting `watcher.NotifyFilter = NotifyFilters.FileName;` do? – techno Jul 21 '16 at 05:36
  • Read this first : https://msdn.microsoft.com/en-us/library/system.io.notifyfilters(v=vs.110).aspx –  Jul 21 '16 at 05:37
  • @x... NotifyFilters.FileName checks for file name change right? – techno Jul 21 '16 at 05:40
  • The Method `Bitmap.FromFile` creates an image and holds the resource. Then you create another image by calling `new Bitmap` that again holding a resource. Second resource you release when exit the `using` block. But the first resource is hold. It also must be released! Use two of the `using` block or remove one unnecessary `Bitmap` creation. – Alexander Petrov Jul 21 '16 at 10:04
  • @AlexanderPetrov Can you please add it as an answer with code. – techno Jul 21 '16 at 10:05

2 Answers2

1

A bit cleaner solution than a Try-Catch might be this one. Im using this code without any exceptions raised.

private static bool IsImage(string path) {
      try {
        var result = false;

        using (var stream = new FileStream(path, FileMode.Open)) {
          stream.Seek(0, SeekOrigin.Begin);

          var jpg = new List<string> { "FF", "D8" };
          var bmp = new List<string> { "42", "4D" };
          var gif = new List<string> { "47", "49", "46" };
          var png = new List<string> { "89", "50", "4E", "47", "0D", "0A", "1A", "0A" };
          var imgTypes = new List<List<string>> { jpg, bmp, gif, png };

          var bytesIterated = new List<string>();

          for (var i = 0; i < 8; i++) {
            var bit = stream.ReadByte().ToString("X2");
            bytesIterated.Add(bit);

            var isImage = imgTypes.Any(img => !img.Except(bytesIterated).Any());
            if (isImage) {
              result = true;
              break;
            }
          }
        }
        return result;
      } catch (UnauthorizedAccessException) {
        return false;
      }
    }

Usage of code

foreach (var file in Directory.EnumerateFiles(@"pathToFlowersFolder"))
            {
                Console.WriteLine($"File: {file} Result:{IsImage(file)}");
            }

Edit

After playing around i got an IO-Exception (File already in use)
After reading this i'd offer you the following solution:

private void button1_Click(object sender, EventArgs e)
        {
            var watcher = new FileSystemWatcher();
            watcher.Created += new FileSystemEventHandler(fileSystemWatcher1_Changed);
            watcher.Path = @"c:\temp";
            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
            watcher.EnableRaisingEvents = true;
        }

        private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
        {
            Thread.Sleep(100); // <- give the Creator some time. Increase value for greate pause
            if (IsImage(e.FullPath))
            {
                Console.WriteLine("success----------->" + e.FullPath);
            }
        }

Note

This piece of code properly works on my machine. My HDD is an SSD, so you might need to increase the thread-sleeping time. It properly works for all images (jpg, bmp, gif, png) up to a size of 7 Mb (im quite sure and greater).

If this code doesnt works for you, please post the exception rather than uploading your code.

Community
  • 1
  • 1
lokusking
  • 7,396
  • 13
  • 38
  • 57
0

For the first requirement: "I only want the program to process image files"

private static void fileSystemWatcher1_Changed(object sender, FileSystemEventArgs e)
{
  string strFileExt = getFileExt(e.FullPath); 

  // filter file types 
  if (Regex.IsMatch(strFileExt, @"\.png|\.jpg", RegexOptions.IgnoreCase)) 
  { 
      //here Process the image file 
  }
} 

For the second requirement: "Out of Memory Exception"

Here what happens is, when the file is created (only file name and some attributes) the system is calling the created event. Then the file changed event is also called

So you have to do the processing in the changed event. Also to prevent duplicate calling you have to add a filter to your watcher.

The following is the complete code.

private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
        {

            FileInfo fileInfo = new FileInfo(e.FullPath);
            string strFileExt = fileInfo.Extension;

            // filter file types 
            if (Regex.IsMatch(strFileExt, @"\.png|\.jpg", RegexOptions.IgnoreCase))
            {
                //here Process the image file 
                try
                {
                    using (Bitmap test = new Bitmap(Bitmap.FromFile(e.FullPath)))
                    {
                        //Do your code here.
                    }
                }
                catch (Exception error)
                {
                    Console.WriteLine("File Error");
                }
            }


        }

        private void Form1_Load(object sender, EventArgs e)
        {
            fileSystemWatcher1.Path = @"C:\Users\Christlin\Desktop\res";

            //To Prevent duplicated calling of changed event
            fileSystemWatcher1.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
        }
Christlin Panneer
  • 1,599
  • 2
  • 19
  • 31
  • Thanks ... you have hooked to changed event and created event... should i hook to both events? – techno Jul 21 '16 at 05:47
  • Based on the OPs requirements, that might not be enough. For example, I could easily disguise an image by giving it another extension (e.g. `.txt`). – Christian.K Jul 21 '16 at 05:48
  • @techno the changed and Form1_Load events only enough – Christlin Panneer Jul 21 '16 at 05:50
  • @Christian.K thanks for the note, in that case we can even process the file without the file extension check, and handle it with the creation of a bitmap part of the code. – Christlin Panneer Jul 21 '16 at 05:55
  • @ChristlinJoseph I still get `A first chance exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll` when copying like 10 images of size more than 1 MB.It seems the old issue still persists.This occurs because the images are not copied fully. – techno Jul 21 '16 at 06:00
  • @Christian.K plz see by above comment ^ – techno Jul 21 '16 at 06:01
  • @techno You need to keep the Changed event only. Here i have tested with 6MB sized images. Can you post here your code and full exception – Christlin Panneer Jul 21 '16 at 06:05
  • @ChristlinJoseph Please get the complete project here http://wikisend.com/download/701754/WindowsFormsApplication1.zip – techno Jul 21 '16 at 06:07
  • @ChristlinJoseph sorry... please check this http://wikisend.com/download/616644/WindowsFormsApplication1.zip... that project was modified. – techno Jul 21 '16 at 06:12
  • @techno 'if (IsImage(e.FullPath))' function call is not required, because we have already checked the file extensions using regx. Remove that part and it will work – Christlin Panneer Jul 21 '16 at 06:13
  • @ChristlinJoseph yeah... that is the modified project.. please check this one http://wikisend.com/download/616644/WindowsFormsApplication1.zip – techno Jul 21 '16 at 06:15
  • @techno The code is working properly for me, Also you can copy the image to the destination folder, by simply copying the file. Here you are creating a bitmap and writing that created bitmap to the disk. So it is involving lot of RAM. My system here have 16GB of RAM. The code works perfectly in my machine. – Christlin Panneer Jul 21 '16 at 06:23
  • @techno try to use `File.Copy(SourcePath, DestPath, true);` Because the code you have sent used 888MB of RAM for 44 hi-res Images. – Christlin Panneer Jul 21 '16 at 06:26
  • @ChristlinJoseph Please see the files im trying with http://wikisend.com/download/868908/Flowers.zip – techno Jul 21 '16 at 06:35
  • @techno Download this code and try to run it. http://wikisend.com/download/831448/FileSystemWatcherDemo.zip I can send screenshots if required – Christlin Panneer Jul 21 '16 at 06:37
  • @ChristlinJoseph I'm using VS2010 .. getting `This project is incompatible with the current version of visual studio` – techno Jul 21 '16 at 06:41
  • @techno I don't have VS2010, I have modified the solution file. Try to open this file, http://wikisend.com/download/154242/FileSystemWatcherDemo.zip I hope it works, if it is not opening then you can view all the source files in vs2010. Then create a new windows forms project. Add a file watcher by dragging it. Then copy the Form1.cs file contents. Change the path according to your system. Then it will work for sure. – Christlin Panneer Jul 21 '16 at 06:51
  • @ChristlinJoseph Does not work.. file system watcher does not trigger any events .. – techno Jul 21 '16 at 07:01
  • @techno have you changed the watcher Path (in the form load event)? – Christlin Panneer Jul 21 '16 at 07:14
  • download the last attachment, and copy and paste the code in a new windows forms application. dont forgot to drag and drop the file watcher in the designer. try as a fresh copy. try to send a report of windows steps recorder – Christlin Panneer Jul 21 '16 at 07:28
  • @techno I have attached the output and the workflow of the program, using Steps Recorder. download it from http://wikisend.com/download/957996/Program_Flow.zip – Christlin Panneer Jul 21 '16 at 07:36
  • @techno okay let me know the results. – Christlin Panneer Jul 21 '16 at 08:34
  • @ChristlinJoseph sure... thanks a lot for your help :) – techno Jul 21 '16 at 08:36
  • @techno Welcome :) – Christlin Panneer Jul 21 '16 at 08:40