My app receives messages from another system to indicate that a file has been posted in a folder and it then checks the folder to copy that file to another location. This works perfectly, but the file gets posted in the folder 20 minutes after the app gets the message. I'd like some help/ ideas on how I can create a delay without stopping the polling for each file.
Eg:
10:00am: Message received that 20180405_file1.pdf created
10:02am: Message received that 20180405_file2.pdf created
10:20am: check folder for 20180405_file1.pdf
10:22am: check folder for 20180405_ file2.pdf
I tried using System.Thread.Timer, and Timespan, but I don’t want the app to wait until first file is read and then process second file. I want it to be continuously processing other messages and read the files
Any help would be great
MSDN:
FileSystemWatcher listens to the file system change notifications and
raises events when a directory, or file in a directory, changes.
Check msdn for more detail.
What you need:
FileSystemWatcher fileWatcher;
private void watch()
{
fileWatcher = new FileSystemWatcher();
fileWatcher.Path = path;
fileWatcher.Created += new FileSystemEventHandler(OnCreated);
fileWatcher.EnableRaisingEvents = true;
}
private void OnCreated(object source, FileSystemEventArgs e)
{
WaitForFile(e.FullPath);
//Copy files to another directory.
}
private void WaitForFile(string fullPath)
{
while (true)
{
try
{
using (StreamReader stream = new StreamReader(fullPath))
{
break;
}
}
catch
{
Thread.Sleep(1000);
}
}
}
OnCreated will hit once I file is created but wait till it is written. There is no need for any timer as FileSystemWatcher will monitor new file creation.
You can use this only there is a particular folder where all your pdf files are generating after you get the message. If it is a common folder where there are other files too, you need to manage a list of pdf files name same you got from the message notification and then copied them based on condition in OnCreated.
Related
I need to create a listener in C# that will watch a shared folder (UNC path) and copy the files with a specific extension (*.json) to a target folder when they arrive. The files can be delayed for about half a minute.
The folder is never empty.
Problems:
The files will arrive in a new sub folder, FileSystemWatcher can't be used since it can't listen to sub folders in a shared folder.
The files needs to be copied and left in the folder, so we need to assure that the same file isn't copied more than once.
Files that are edited/updated needs to be copied again and overwritten in the target folder.
Other files will be in the folder and new files will arrive that we need to ignore (doesn't have the right extension).
I thought about polling the folder , but I didn't come up with a good implementation.
I'm pretty sure I can't use the FilesystemWatcher object, but maybe someone can find a smart solution for using it.
One solution to your problem could be you can check the location constantly for a while and examine changes by yourself.
it is not a complete solution, but an idea to consider.
public async Task FindMyFile(string filePath)
{
int retries = 0;
this.Founded = false;
while (!this.Founded)
{
if (System.IO.File.Exists(filePath))
this.Founded = true;
else if (retries < this.maxTries)
{
Console.WriteLine($"File {filePath} not found. Going to wait for 15 minutes");
await Task.Delay(new TimeSpan(0, 15, 0));
++retries;
}
else
{
Console.WriteLine($"File {filePath} not found, retries exceeded.");
break;
}
}
}
Excel files are dropped manually into a local folder, there is a FileWatcher which converts the file into a new filestructure and moves it to the next folder which also have a filewatcher. The problem is that when this file is moved to the next folder the filewatcher does not fire any event. However if i cut it and drop it physically the event fires.
I am using File.Move to copy file from folder1 to folder2
you should look at FileSystemWatcher detect when file is moved to folder
Actually when there is a move, the filesystemwatcher send a delete (in the source directory watcher) and a create (in the target directory watcher).
try to use renamed event.
Another reason could be the buffer size may exceeded.
Public void WatchItBaby()
{
// ...
FileSystemWatcher watcher = new FileSystemWatcher(#"c:\temp\", "*.txt");
watcher.Created += new FileSystemEventHandler(OnChangedOrRenamed);
watcher.Renamed += new RenamedEventHandler(OnChangedOrRenamed);
watcher.EnableRaisingEvents = true;
// ...
}
private void OnChangedOrRenamed(object source, FileSystemEventArgs e)
{
// stuff
}
I am new to c# so please forgive my ignorance, I am running a fileSystemWatcher on a text file. And it is working fine, I can do some simple tasks after the file has changed. All but what I want to do.
I am trying to read the last line of the text file that has changed with this code
public void File_Changed( object source, FileSystemEventArgs e )
{
string MACH1 = File.ReadText(#"C:\MACHINE_1.txt").Last();
if (MACH1=="SETUP")
{
MACHINE1IND.BackColor = Color.Green;
}
else
{
MACHINE1IND.BackColor = Color.Red;
}
}
It works fine inside a button but not after file watcher.
Says it cannot find file?
One thing to be aware of is that the FSW can issue multiple change notifications during a save operation. You have no way of knowing when the save is complete. As a result, you need to always wrap your code in a try..catch block and support retry after a timeout to allow the file write to be completed. Typically, I will try to move the file to a temp location where I will do my processing. If the move fails, wait a couple seconds and try again.
As Jim Wooley explains in his answer, the file operation might still be in progress, when FSW fires a Created or Changed event. If FSW is used to communicate between two applications and you are in control of the "sending" application as well, you can solve the problem as follows:
Write the information to a temporary file. Close the file. Rename the temporary file and give it a definitive name.
In the other application (the receiver) watch for the Renamed event using the FileSystemWatcher. The renamed file is guaranteed be complete.
You'll have to check if the file exists before accessing it.
public void File_Changed(object source, FileSystemEventArgs e)
{
string filePath = #"C:\MACHINE_1.txt";
if(!File.Exists(filePath)) //Checks if file exists
return;
string MACH1 = File.ReadText(filePath).Last();
if (MACH1=="SETUP")
{
MACHINE1IND.BackColor = Color.Green;
}
else
{
MACHINE1IND.BackColor = Color.Red;
}
}
My application is listening to directory and each new file who created need to handle with, so I did a test and listen to my folder and see that if I move big file into this folder the event fired before the whole file is created and this can cause my problem.
can I wait until the all file is created ?
public void startListener(string directoryPath)
{
FileSystemWatcher watcher = new FileSystemWatcher(directoryPath);
watcher.Filter = "*.avi";
watcher.Created += watcher_Created;
watcher.EnableRaisingEvents = true;
}
void watcher_Created(object sender, FileSystemEventArgs e)
{
}
What I think you could do, is handle the OnCreated event, by adding the "newly created" files to a local List.
Then on the OnChanged, which will trigger when pieces are updated, check if the file is in that list of newly created ones, and attempt to open the file exclusively, doing something like:
File.Open("someFile.avi", FileMode.Open, FileAccess.Read, FileShare.None)
If you get an IOException, that file is still in use.
Another thing you could do is described in this comment, using the Rename event:
https://stackoverflow.com/a/5894697/1373170
In fact that whole SO question might have useful information for you:
C# FileSystemWatcher, How to know file copied completely into the watch folder
According the the MSDN docs, multiple OnCreate() and OnChanged() events may be generated when a file is copied from one directory to another.
We need to distinguish two cases here:
You are copying the file yourself, so you have control over how the copying is done.
In this case, it's most efficient to use a temporary filename in the desired folder which does not have the .avi extension (you could for instance use filename.avi.tmp instead of filename.avi), then rename it to the correct name with the .avi extension when you're done copying.
Then, you subscribe to the Renamed event and watch for files that are renamed to .avi.
If you don't have any control over the copying, then you could use one of the techniques described in another answer to this question
I have a program that roughly does this:
open a file to read from it.
close the file
Start a filewatcher to watch for changes in the file.
As soon as a change is detected, the filewatcher's EnableRaisingEvents flag is set to false and the process repeats from Step 1.
The problem is, after going from step 4 to step 1, it cannot read the file saying that it is being used by another Process.
Error I receive:
Unhandled Exception: System.IO.IOException: The process cannot access the file 'c:\test.xml' because it is being used by another process.
Whats going wrong? does the reader from Step 1 of my program still have the file open, or is some entirely different process accessing the file, or is it that filewatcher is still watching the file after moving to Step 1 from 4, despite setting the flag to false?
If your code is similar to this:
[STAThread]
static void Main(string[] args)
{
string file = "temp.txt";
ReadFile(file);
FileSystemWatcher fswatcher = new FileSystemWatcher(".\\");
fswatcher.Changed += delegate(object sender, FileSystemEventArgs e)
{
ReadFile(e.FullPath);
};
while (true)
{
fswatcher.WaitForChanged(WatcherChangeTypes.Changed);
}
}
private static void ReadFile(string file)
{
Stream stream = File.OpenRead(file);
StreamReader streamReader = new StreamReader(stream);
string str = streamReader.ReadToEnd();
MessageBox.Show(str);
streamReader.Close();
stream.Close();
}
If you are editing the file via notepad, then, when you click the save button, it keeps the file open, while as if when you just close the program and click save it doesn't. I do no know if this is a bug or an undocumented feature of notepad, but this just might be your problem. One way to fix this is to do the following:
In your anonymous delegate, or wherever you execute the call to ReadFile() call Thread.Sleep(1000), to have the program wait before reading the file and your code should work fine.
You can use a tool like Process Explorer from http://www.sysinternals.com to see who has the open handle to the process
The file is most likely held open by whatever caused the change notification to fire in the first place.
Beside other answers it is possible that when FileWatcher reacts file it not yet closed by that app. In step 1 try not to fail immediately but try several attempts with small delay.
Note: even if "file.txt" is open in Notepad, this code still works, because it is opening for read.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
ReadFromFile(#"C:\file.txt");
Console.ReadLine();
}
static void ReadFromFile(string filename)
{
string line;
using (StreamReader sr = File.OpenText(filename))
{
line = sr.ReadLine();
while (line != null)
{
Console.WriteLine(str);
line = sr.ReadLine();
}
sr.Close();
}
}
}
Or just:
string text = System.IO.File.ReadAllText(#"C:\file.txt");
The problem is that the FileSystemWatcher tells you immediately when the file was created. It doesn't wait for the file to be released.
For instance, when you copy a large file which takes 3 seconds to copy, so you have to open the file after that.
http://www.codeproject.com/Questions/461666/FileSystemWatcher-issue-in-windows-application
Wait until file is unlocked in .NET
There are a number of things that could be going on.
First, make sure you properly dispose of the file writer (close isn't good enough) by utilizing the using clause around everything that implements IDisposable.
Second, it you are simply reading, make sure you have the correct flags set when opening the file.
To go any further it would help if you provided a code block which showed how you were accomplishing this; particularly around the reading of the file
You can use this MS utility openfiles to obtain list of opened files and understand who has opened the file.
openfiles /query
Also it allow to disconnect files opened by network users.
openfiles /disconnect /id XXXX
If you want use it for local PC you should set Maintain Objects List global flag:
openfiles /local on
Follow the link to get more details.