File written 3 times C# - c#

I have this code that is being repeated 3 times:
private static void convert(object source, FileSystemEventArgs f)
{
string FileName;
FileName = f.FullPath;
string destinationFile = #"Y:\test\test.xml";
System.Threading.Thread.Sleep(2000);
try
{
Encoding utf8 = new UTF8Encoding(false);
Encoding ansi = Encoding.GetEncoding(1256);
System.Threading.Thread.Sleep(2000);
string xml = File.ReadAllText(FileName, ansi);
XDocument xmlDoc = XDocument.Parse(xml);
**Console.WriteLine("1st");**
File.WriteAllText(
destinationFile,
#"<?xml version=""1.0"" encoding=""utf-8""?>" + xmlDoc.ToString(),
utf8
);
}
Check the above in bold. It writes out 3 times. I have just put that to test. But why does it write out 3 times.. Meaning the file being written is also written 3 times.
I'm calling this function from a filesystemwatcher function to watch a folder if it has changed then take the file convert it to utf-8 and put it in the destination file.
EDIT 1:
Here is my watcher. Can you please check if this is fine:
private static void WatchFile()
{
watcher.Path = #"C:\project";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.xml";
watcher.Changed += new FileSystemEventHandler(convert);
watcher.Error += new ErrorEventHandler(WatcherError);
Console.WriteLine("2nd");
watcher.EnableRaisingEvents = true;
}
Still don't have a clue why it gets repeated 3 times.
EDIT 2:
Here goes' my complete code:
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
class Test
{
class Class1
{
private static FileSystemWatcher watcher =
new FileSystemWatcher();
public static void Main()
{
WatchFile();
Console.ReadLine();
}
private static void WatchFile()
{
watcher.Path = #"C:\project";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.xml";
watcher.Changed += new FileSystemEventHandler(convert);
watcher.Error += new ErrorEventHandler(WatcherError);
Console.WriteLine("2nd");
watcher.EnableRaisingEvents = true;
}
public static string CrL = "\r\n";
private static void convert(object source, FileSystemEventArgs f)
{
string FileName;
FileName = f.FullPath;
string destinationFile = #"Y:\test\OnAirNow.xml";
System.Threading.Thread.Sleep(2000);
try
{
Encoding utf8 = new UTF8Encoding(false);
Encoding ansi = Encoding.GetEncoding(1256);
System.Threading.Thread.Sleep(2000);
string xml = File.ReadAllText(FileName, ansi);
XDocument xmlDoc = XDocument.Parse(xml);
Console.WriteLine("1st");
File.WriteAllText(
destinationFile,
#"<?xml version=""1.0"" encoding=""utf-8""?>" + xmlDoc.ToString(),
utf8
);
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
}
private static void WatcherError(object source, ErrorEventArgs e)
{
Exception watchException = e.GetException();
watcher = new FileSystemWatcher();
while (!watcher.EnableRaisingEvents)
{
try
{
WatchFile();
Console.WriteLine("I'm Back!!");
}
catch
{
System.Threading.Thread.Sleep(2000);
}
}
}
}
}

A common pattern using FileSystemWatcher is to set EnableRaisingEvents to false when starting processing the event:
this.fileSystemWatcher = new FileSystemWatcher()
{
Path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
NotifyFilter = NotifyFilters.LastWrite,
Filter = Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
};
this.fileSystemWatcher.Changed += this.ConfigChanged;
this.fileSystemWatcher.EnableRaisingEvents = true;
and
public void ConfigChanged(object sender, FileSystemEventArgs e)
{
try
{
this.fileSystemWatcher.EnableRaisingEvents = false;
s_logger.Info("Configuration file changed.");
// reload config here
s_logger.Info("Configuration settings reloaded.");
}
catch (Exception exception)
{
s_logger.Error(exception.Message);
s_logger.Error("Failed to reload configuration settings.");
}
finally
{
this.fileSystemWatcher.EnableRaisingEvents = true;
}
}

The FileSystemWatcher might raise more than one event for one file change, check it out:
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.
FileSystemWatcher Class in MSDN

Related

fileSystemWatcher doesn't work when watching for created zip files

I have been using fileSystemWatcher for c# to track when a zip file has been downloaded, the problem is that the name of the file never gets output to result.Name. Why is this??
public ResultsDetails ExportCallsToCsv()
{
var downloadsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + #"\Downloads";
ExportToCsvButton.Click();
using (var watcher = new FileSystemWatcher(downloadsDirectory))
{
watcher.Filter = "*.zip";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
CsvFilePath = $#"{downloadsDirectory}\{result.Name}".Replace("_", "-");
}
return new ResultsDetails(ActionResult.Passed, "Csv file created succesfully.");
}
Why does result.Name never get filled? I've attempted to put an endless while loop inside the using block which breaks when result.Name gets filled, but to still no avail. It just halts forever when I do that.
FileSystemWatcher.WaitForChanged is a synchronous/blocking method and is unable to capture the file change that was triggered on the same thread.
Here's an example of your code in a small test program. This will not work (as you pointed out).
static void Main(string[] args)
{
var directory = "C:\\test";
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
using (var watcher = new FileSystemWatcher(directory))
{
watcher.Filter = "*.txt";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
if (result.Name is null)
{
// This is always printed.
Console.WriteLine("No name");
}
}
Console.ReadLine();
}
Here's a small sample program that does work. This version handles the FileSystemWatcher events asynchronously.
static void Main(string[] args)
{
var directory = "C:\\test";
using (var watcher = new FileSystemWatcher(directory, "*.txt"))
{
watcher.EnableRaisingEvents = true; // *** Be sure to include this!
watcher.Created += Watcher_Created; // *** This is different, too.
File.Delete("C:\\test\\new_file.txt");
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
}
Console.WriteLine("All done.");
Console.ReadLine();
}
private static void Watcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Detected {e.Name} is {e.ChangeType}.");
}

FileSystemWatcher Not recognizing change in directory

So I am trying to create a console application that will watch a directory and when a folder is dropped into the directory it will optimize the files inside of the folder. The optimization part is working. I am currently concerned with the fact that once I run the program with the FileSystemWatcher it never recognizes the change. Is the class only focusing on the root directory and not going any deeper?
I also saw on Microsoft's website that the way to watch for a file that has been copy pasted or moved into the directory is by using FileSystemWatcher.Renamed instead of .Changed which is what I was having an issue with before.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Runtime.CompilerServices;
using System.Threading;
using pdftron;
using pdftron.Common;
using pdftron.Filters;
using pdftron.SDF;
using pdftron.PDF;
using OptimizerTestCS;
namespace PDFNetSamples
{
class Class1
{
private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
public static void Optimize()
{
Thread.Sleep(1000);
PDFNet.Initialize();
string input_Path = #"C:\Users\user\Desktop\testinp\";
string output_Path = #"C:\Users\user\Desktop\output\";
string[] files = Directory.GetFiles(input_Path, "*.pdf", SearchOption.AllDirectories);
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
Console.WriteLine($"Optimizing {fileName}");
string sub = file.Substring(35, 7);
CreateFolder(output_Path + sub);
try
{
using (PDFDoc doc = new PDFDoc(file))
{
doc.InitSecurityHandler();
Optimizer.Optimize(doc);
doc.Save(output_Path + sub + fileName, SDFDoc.SaveOptions.e_linearized);
Console.WriteLine("Done..\n");
}
}
catch (PDFNetException e)
{
Console.WriteLine(e.Message);
}
}
}
private static void Run()
{
string[] args = Environment.GetCommandLineArgs();
if (args.Length != 2)
{
Thread.Sleep(3000);
Console.WriteLine("Usage: Watcher.exe (directory)");
return;
}
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
watcher.Path = #"C:\Users\user\Desktop\testinp\";
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
watcher.Filter = "*.pdf";
watcher.Renamed += OnChanged;
watcher.EnableRaisingEvents = true;
Console.WriteLine("Press 'q' to quit the sample.");
while (Console.Read() != 'q') ;
}
}
private static void OnChanged(object source, FileSystemEventArgs e) =>
Optimize();
static void CreateFolder(string path)
{
Directory.CreateDirectory(path);
}
public static void Main(string[] args)
{
while (true)
{
Run();
}
}
}
}
Add watcher.IncludeSubdirectories = true; to include sub directories.
You need to set the IncludeSubdirectories property to true.
Like others already said, you need to add watcher.IncludeSubdirectories = true;.
I guess your Optimize Method is not working cause of the input_path, which should be different for sub-directories as it's no longer input_path + filename.
So that said, you need to adjust your input_path to match the directory which you are tracking at the moment.

System.Collections.ListDictionaryInternal Error

I'm trying to create a program where every time i create a text file,it will compress the file and log it .
It works for the first text file but when i create the second one, i get this exception: System.Collections.ListDictionaryInternal
Error:The process cannot access the file 'D:\TemeC#\FilesHomework\FilesHomework\ obj\Debug\New Text Document.txt' because it is being used by another process.
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.IO.Compression;
using System.Security.Permissions;
namespace FilesHomework
{
class Program
{
static void Main(string[] args)
{
Run();
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public static void Run()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "D:";
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "*.txt";
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
Console.WriteLine("Press \'q\' to quit the sample.");
while (Console.Read() != 'q') ;
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
try {
FileInfo FileIn = new FileInfo(e.Name);
Compress(FileIn);
// FileStream LogFile = File.Open("LogFile.txt",FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.ReadWrite);
// File.SetAttributes("LogFile.txt", FileAttributes.Normal);
// StreamWriter sw = new StreamWriter(LogFile);
string str;
str = ("The file " + e.Name + " has been deleted at " + DateTime.Now);
// byte[] b1 = System.Text.Encoding.UTF8.GetBytes(str);
// sw.WriteLine(str);
Console.WriteLine(str);
File.Delete(e.Name);
// LogFile.Close();
// sw.Close();
}
catch(Exception er)
{
Console.WriteLine("Error:" + er.Data);
}
}
public static void Compress(FileInfo fileSelected)
{
using (FileStream originalFileStream = fileSelected.OpenRead())
{
if ((File.GetAttributes(fileSelected.FullName) &
FileAttributes.Hidden) != FileAttributes.Hidden & fileSelected.Extension != ".gz")
{
using (FileStream compressedFileStream = File.Create(fileSelected.Name+ ".gz"))
{
using (GZipStream compressionStream = new GZipStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
}
}
}
}
}
What do you guys think i should do?
Ahh, I think I see the problem.
It looks like your code is trying to pick up the new file, before you actually close the file in your editor.
You need to introduce a mechanism into your Compress method that checks if the file is available to open first.
See this answer to check if a file can be accessed or not, in code:
Is there a way to check if a file is in use?
Edit:
I would also try changing the FileSystemWatcher event you use to be .Changed, which fires after a file attribute is changed.
I would still check you can Open it to read by the link above though.

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...
Set a timeout after last write when it is assumed there are no more changes coming
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();
}
}

exception rise with FileSystemWatcher

I wrote small app for monitoring file for changes.
When I run it, every time I get exception about Path. And I can't understand why. The path is really exist.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Run();
}
public static void Run()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"D:\test\1.txt";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed +=new FileSystemEventHandler(watcher_Changed);
watcher.EnableRaisingEvents = true;
}
static void watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType);
}
}
}
FileSystemWatcher.Path should be a Path not a file name
watcher.Path = #"D:\test";
watcher.Filter = "1.txt";
private static void watcher_Changed(object source, FileSystemEventArgs e)
{
// this test is unnecessary if you plan to monitor only this file and
// have used the proper constructor or the filter property
if(e.Name == "1.txt")
{
WatcherChangeTypes wct = e.ChangeType;
Console.WriteLine("File {0} {1}", e.FullPath, wct.ToString());
}
}
You can also limit the monitoring using the constructor that takes two parameters, the path and a file filter.
FileSystemWatcher watcher = new FileSystemWatcher(#"d:\test", "1.txt");
See MSDN References

Categories