FileSystem Watcher- Checking if copied file is an Image - c#

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.

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.

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

Related

How can I load multiple images using LoadAsync() in C#?

I'm trying to update the GUI, and I have an asynchronous function that uses LoadAsyc(), when I load just one image, it works but when I try to load more than one, the second one doesn't display.
This my code:
public UserFriendlyInterface()
{
InitializeComponent();
locationFileH5 = "";
serverStatus = false;
ipAddress = getLocalIPAddress();
port = 5000;
watcher = new FileSystemWatcher(#"flask_server\cnn\_prepImages_");
watcher.EnableRaisingEvents = true;
watcher.Changed += watcher_Changed;
}
private void watcher_Changed(object sender, FileSystemEventArgs e)
{
updateImages();
}
async Task updateImages()
{
pictureBoxNormalImg.WaitOnLoad = false;
pictureBoxNormalImg.LoadAsync(#"flask_server\cnn\_prepImages_\normal.jpg");
pictureBoxSegmentation.WaitOnLoad = false;
pictureBoxSegmentation.LoadAsync(#"flask_server\cnn\_prepImages_\segmentation.jpg");
}
What you are trying to achieve can be achieved more robustly by querying the Name property of the FileSystemEventArgs object, and updating only the corresponding PictureBox.
private static void Watcher_Changed(object sender, FileSystemEventArgs e)
{
PictureBox pictureBox;
switch (e.Name.ToLowerInvariant())
{
case "normal.jpg": pictureBox = pictureBoxNormalImg; break;
case "segmentation.jpg": pictureBox = pictureBoxSegmentation; break;
default: pictureBox = null; break;
}
if (pictureBox != null)
{
Image image = null;
try
{
using (var temp = new Bitmap(e.FullPath))
{
image = new Bitmap(temp);
}
}
catch { } // Swallow exception
if (image != null)
{
pictureBox.Invoke((MethodInvoker)(delegate ()
{
pictureBox.Image = image;
}));
}
}
}
I would avoid the LoadAsync method because it is intended mainly for loading images from the internet, and because I don't totally trust it.
Update: There were two problems with my initial code:
1) Free file locked by new Bitmap(filePath)
2) FileSystemWatcher Changed event is raised twice
The updated code solves these problems (hopefully), but not in the most robust or efficient way possible.
Update: To make the code more efficient, by avoiding the repeated loading of the images caused by multiple firings of the Changed event, you could use the extension method OnChanged found in this answer. It suffices to replace the line below:
watcher.Changed += Watcher_Changed;
...with this one:
watcher.OnChanged(Watcher_Changed, 100);

FileSystemWatcherChanged event dosen't fire for a memory mapped file

I have json file that I want to share between two processess. So I created a memory mapped file as follows.
private void CreateMemoryMappedFile()
{
var info = Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "/" + model.Data.Settings.OrcaUISpecificSettings.TimeOutFolder);
string path = Path.Combine(info.FullName + #"\" + model.Data.Settings.OrcaUISpecificSettings.File);
FullPath = path;
try
{
mmf = MemoryMappedFile.CreateFromFile(path, FileMode.CreateNew, "MyMemoryFile", 1024 * 1024);
}
catch (Exception ex)
{
}
}
public MemoryMappedViewStream GetAccessor()
{
MemoryMappedViewStream FileMapView = null;
if (FileMapView != null)
{
return FileMapView;
}
FileMapView = mmf.CreateViewStream();
return FileMapView;
}
And to read and write to the files I am doing the following
public void WriteToMemoryMappedFile(string Data)
{
try
{
mutex.WaitOne();
byte[] bytes = Encoding.ASCII.GetBytes(Data);
var accessor = GetAccessor();
accessor.Write(bytes, 0, Data.Length);
mutex.ReleaseMutex();
}
catch (Exception ex)
{
}
}
public string ReadFromMemoryMappedFile()
{
mutex.WaitOne();
var accessor = GetAccessor();
using (BinaryReader binReader = new BinaryReader(accessor))
{
byte[] reader = binReader.ReadBytes((int)accessor.Length);
string result = Encoding.Default.GetString(reader);
mutex.ReleaseMutex();
return result.Replace("NULL", "");
}
}
My problem is I have an Activity Monitor for my app. So after x amount of time I am updating the json file with InActiveStatus. Along the same lines I am listening to any file changes(Look at D_IDle event). Problem is If a normal file is changed I get the FileSytemWatcher changed event firing just fine. But when I use a Memory Mapped File to update the status, the FileSystemWatcher changed event never gets fired please help.
private void D_IsIdle(object sender, EventArgs e)
{
MonitorDirectory();
//AppViewModel.SerializeData("InActive");
AppViewModel.SerializeDataToMemoryMap("InActive");
d.IsIdle -= D_IsIdle;
}
public void MonitorDirectory()
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(AppViewModel.GetDriectory());
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Filter = "*.json";
fileSystemWatcher.Changed += FileSystemWatcher_Changed;
fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
//IT NEVER COMES HERE
}
Using FileSystemWatcher with memory mapped files must be some kind of anti-pattern:). If you are on a local system, use one of the process synchronization primitives (e.g. semaphores) to signal change.
My guess is that the FileSystemWatcher triggers when the file handle is closed to avoid other processed reading partially written files.

C# FileSystemWatcher Copy folder complete

I am using FileSystemWatcher to monitor a folder that will be used to do some file renaming.
The only thing that will be copied will be folders containing files. There will not be single files put into the monitored folder. This is the code for setting up the FileSystemWatcher
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
watcher.IncludeSubdirectories = true;
watcher.Filter = "*.*";
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Renamed += new RenamedEventHandler(watcher_Renamed);
watcher.EnableRaisingEvents = true;
There doesn't seem to be any issues with this setup..
The folders being copied can be between 50-200mb big. Is there a way to check/make sure that all the files have completed copying before starting the renaming process.
I tried this thinking that i would get an IOException if the copying was still happening when the GetFiles() was called.
bool finishedCopying = false;
while (!finishedCopying)
{
try
{
List<FileInfo> fileList = directoryInfo.GetFiles().ToList();
AlbumSearch newAlbum = new AlbumSearch(directoryInfo);
return newAlbum;
}
catch (IOException)
{
finishedCopying = false;
}
}
If anymore information is required, just ask an i can provide.
Ta.
I gave this a go using a timer. It may not be the prettiest solution out there but at first testing it seems to be working so far. Essentially what this does is when a folder is copied to the monitored folder it will add the folder path to the AlbumList. The files in that folder will trigger the Created event. This waits for the file to finish copying. Once finished it starts a timer. If a new Created event gets triggered the timer will reset itself.
When the timer.elapsed event is triggered it assumes (and I know assumption is the mother of all f*&k ups) that there are no more files to be copied and can start to process the fully copied folder..
System.Timers.Timer eventTimer = new System.Timers.Timer();
List<string> AlbumList = new List<string>();
private void watcher_Created(object sender, FileSystemEventArgs e)
{
if (Directory.Exists(e.FullPath))
{
AlbumList.Add(e.FullPath);
}
if (File.Exists(e.FullPath))
{
eventTimer.Stop();
FileInfo newTrack = new FileInfo(e.FullPath);
while (IsFileLocked(newTrack))
{
// File is locked. Do Nothing..
}
eventTimer.Start();
}
}
private void eventTimer_Elapsed(object sender, ElapsedEventArgs e)
{
List<string> ItemToRemove = new List<string>();
foreach (var item in AlbumList)
{
DirectoryInfo di = new DirectoryInfo(item);
AlbumSearch newAlbum = new AlbumSearch(di);
if (DoSomethingMethod(newAlbum))
{
ItemToRemove.Add(item);
}
else
{
// why did it fail
}
}
foreach (var path in ItemToRemove)
{
AlbumList.Remove(path);
}
}
private bool DoSomethingMethod(AlbumSearch as)
{
// Do stuff here
return true;
}
This is a small demo app that check files at the beginning, and then uses two hashsets to track copied files. This will only work if the source directory is known. There is no way to know if a file was created from a file copy or from a direct creation, so you can only compare two known directories with Directory.GetFiles. And, as already said in comments, you will still have to check if during the copy process, other files were added / removed / renamed in the old directory
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static HashSet<string> oldDirFiles = new HashSet<string>();
static HashSet<string> newDirFiles = new HashSet<string>();
static string oldDir = "C:\\New Folder";
static string newDir = "C:\\New Folder 2";
static System.Threading.ManualResetEvent resetEvent = new System.Threading.ManualResetEvent(false);
static void Main(string[] args)
{
System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher();
watcher.Path = newDir;
watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
watcher.IncludeSubdirectories = true;
watcher.Filter = "*.*";
watcher.Created += watcher_Created;
watcher.Changed += watcher_Changed;
watcher.Renamed += watcher_Renamed;
watcher.EnableRaisingEvents = true;
//get all files in old directory
var oldFiles = Directory.GetFiles(oldDir, "*.*", SearchOption.AllDirectories);
foreach (var file in oldFiles)
oldDirFiles.Add(file);
resetEvent.WaitOne();
//now launch the directory copy
//then you have to check if in the meaning time, new files were added or renamed
//that could be done also with a watcher in the old directory
}
static void watcher_Renamed(object sender, RenamedEventArgs e)
{
throw new NotImplementedException();
}
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
throw new NotImplementedException();
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
//check if the copied file was in the old directory before starting
if (oldDirFiles.Contains(e.FullPath.Replace(newDir, oldDir)))
{
newDirFiles.Add(e.FullPath);
//if all the files have been copied, the file count will be the same in the two hashsets
//the resetevent.Set() signal the waiting thread and the program can proceed
if (newDirFiles.Count == oldDirFiles.Count)
resetEvent.Set();
}
}
}
}

Capture class (Emgu) issue in multiple background worker threads

I have a simple Winforms app that allows users to select multiple videos (files) simultaneously and runs background workers threads to loop through each of the videos in the BW. Have pasted code below, I get a NullReferenceException as "Unable to create capture from ..." at this line
Capture _capture = new Capture(videoFileName)
in processVideo method.
N.B: The same code work fine if I select a single video. So some issue with the multiple instances of Capture class.
I would expect the ProcessVideo method to have new instance of Capture and open it separately. Any ideas on what I might be doing wrong?
private void openVideoToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Video | *.AVI;*.MPEG;*.WMV;*.MP4;*.MOV;*.MPG;*.MPEG;*.MTS;*.FLV";
ofd.Multiselect = true;
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] videos = ofd.FileNames;
if (videos != null)
{
BackgroundWorker[] bw = new BackgroundWorker[videos.GetLength(0)];
int n = 0;
foreach (string video in videos)
{
bw[n] = new BackgroundWorker();
bw[n].DoWork += new DoWorkEventHandler(bw_DoWork);
bw[n++].RunWorkerAsync(video);
}
}
}
}
catch (NullReferenceException excpt)
{
MessageBox.Show(excpt.Message);
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
string filename = (string)e.Argument;
ProcessVideo(filename);
}
private void ProcessVideo(string videoFileName)
{
Capture _capture = new Capture(videoFileName);
UInt64 TOTAL_FRAMES = Convert.ToUInt64(_capture.GetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_COUNT));
for (UInt64 n = 0; n < TOTAL_FRAMES; n++)
{
using (Image<Bgr, Byte> img1 = _capture.QueryFrame())
{
//do something with the frame
}
}
}
I suggest you to update Sourcesafe service pack
it may help you
[I think you code is perfect there is
nothing wrong in it.
You got an error while creating object it clearly saw that
there may be chance that file format is not supported
or may be internal error problem.]
Let me know that after doing updation it works or not.
Regards Red

Filesystemwatcher double entries

I made a small winforms application to monitor a certain folder for new pdf files, if a new pdf file is created in the particulair folder it will copy it to an other location.
The problem i'm having is that the filesystemwatcher creates double/multiple entries in my listbox, how can i solve this?
namespace Scanmonitor
{
public partial class Form1 : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
DateTime lastRead = DateTime.MinValue;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FileWatch();
}
public void FileWatch()
{
watcher.Path = #"C:\Scanner";
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite;
watcher.Filter = "*.pdf";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
public void OnChanged(object source, FileSystemEventArgs e)
{
scannerListBox.Items.Add(e.FullPath);
scannerListBox.SelectedIndex = scannerListBox.Items.Count - 1;
FileMove(scannerListBox.SelectedItem.ToString());
}
public void FileMove(string filePath)
{
try
{
System.IO.File.Copy(filePath, #"\\share\Data\Scans op OCE 600\" + Path.GetFileName(filePath));
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
}
}
}
}
Got it working.
public void OnChanged(object source, FileSystemEventArgs e)
{
try
{
watcher.EnableRaisingEvents = false;
FileInfo objFileInfo = new FileInfo(e.FullPath);
if (!objFileInfo.Exists) return;
System.Threading.Thread.Sleep(5000);
FileInfo fileinformatie = new FileInfo(e.FullPath);
string strCreateTime = fileinformatie.CreationTime.ToString();
string strCreateDate = fileinformatie.CreationTime.ToString();
strCreateTime = strCreateTime.Remove(strCreateTime.LastIndexOf(" "));
strCreateDate = strCreateDate.Remove(0,strCreateDate.LastIndexOf(" "));
ProcessAllFiles(e.FullPath, strCreateTime, strCreateDate);
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
finally
{
watcher.EnableRaisingEvents = true;
}
}
You need to keep track of files (in a collection or dictionary) that already raised an event by the FileSystemWatcher. According to MSDN
Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
public void OnChanged(object source, FileSystemEventArgs e)
{
try
{
watcher.EnableRaisingEvents = false;
FileInfo objFileInfo = new FileInfo(e.FullPath);
if (!objFileInfo.Exists) return;
System.Threading.Thread.Sleep(5000);
FileInfo fileinformatie = new FileInfo(e.FullPath);
string strCreateTime = fileinformatie.CreationTime.ToString();
string strCreateDate = fileinformatie.CreationTime.ToString();
//Ignore this, only for my file information.
strCreateTime = strCreateTime.Remove(strCreateTime.LastIndexOf(" "));
strCreateDate = strCreateDate.Remove(0,strCreateDate.LastIndexOf(" "));
ProcessAllFiles(e.FullPath, strCreateTime, strCreateDate);
}
catch (Exception ex)
{
ToolLabel.Text = ex.Message.ToString();
}
finally
{
watcher.EnableRaisingEvents = true;
}
}

Categories

Resources