using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace Yaulw.File { /// /// A File SystemWatcher utility that functions like the infamous unix tail utility, /// with a callback when the file get's created or updated. /// public class Tail { #region Public Events /// /// Event Delegate for the Incoming Data Event /// /// object reference to the sending Tail Object /// change data / or full data if bIsNewFile is true /// true if a new file was created public delegate void IncomingDataHandler(object sender, string newData, bool bIsNewFile); /// /// Caller must subscribe to the Incoming Data Event /// public event IncomingDataHandler IncomingData = null; #endregion #region Private Members private string _FileNameNPath = ""; private FileSystemWatcher _fileSystemWatcher = null; private long _previousSeekPosition = 0; private object _lockingObj = new Object(); private const int MAX_BYTE_TO_READ_IN_ONE_TIME = 1024 * 16; #endregion #region Construction /// /// Construct a new Tail Object to monitor the specified file /// /// File to Monitor with Tail public Tail(string FileNameNPath) { _previousSeekPosition = 0; _FileNameNPath = FileNameNPath; } #endregion #region Public Methods /// /// Start Monitoring the File /// public void StartMonitoring() { // Get File Information * Setup System Watcher * FileInfo targetFile = new FileInfo(_FileNameNPath); _fileSystemWatcher = new FileSystemWatcher(); _fileSystemWatcher.IncludeSubdirectories = false; _fileSystemWatcher.Path = targetFile.DirectoryName; _fileSystemWatcher.Filter = targetFile.Name; _fileSystemWatcher.Created += new FileSystemEventHandler(TargetFile_Created); _fileSystemWatcher.Changed += new FileSystemEventHandler(TargetFile_Changed); // Perform an Initial read, if the file exists if (targetFile.Exists) TargetFile_Created(null, null); // Start up File System Watcher _fileSystemWatcher.EnableRaisingEvents = true; } /// /// Stop Monitoring the File /// /// set to false, if you are running into trouble during a shutdown public void StopMonitoring(bool bDispose = true) { _fileSystemWatcher.EnableRaisingEvents = false; if(bDispose) _fileSystemWatcher.Dispose(); _fileSystemWatcher = null; } /// /// To read a file from Beginning to End. Call this function, /// if you want to reset the position counter and start reading again from the beginning. /// /// the Contents of the File public StringBuilder ReadFromBeginningToEndOfFile() { _previousSeekPosition = 0; return ReadFileContentsTillEndStartingFromPreviousSeekPosition(); } #endregion #region Private Helper Methods /// /// Handled File System Watcher's Created Callback /// /// SystemWatcher object /// SystemWatcher event args private void TargetFile_Created(object source, FileSystemEventArgs e) { // Read the File Contents StringBuilder sb = ReadFromBeginningToEndOfFile(); //call delegate with the string if (IncomingData != null && sb.Length > 0) IncomingData(this, sb.ToString(), true); } /// /// Handled File System Watcher's Changed Callback /// /// SystemWatcher object /// SystemWatcher event args private void TargetFile_Changed(object source, FileSystemEventArgs e) { // File size size changed to less! ~someone must have // changed content inside of it FileInfo targetFile = new FileInfo(_FileNameNPath); bool bIsNew = false; if (targetFile.Length < _previousSeekPosition) { _previousSeekPosition = 0; bIsNew = true; } // Read the File Contents StringBuilder sb = ReadFileContentsTillEndStartingFromPreviousSeekPosition(); //call delegate with the string if(IncomingData != null && sb.Length > 0) IncomingData(this, sb.ToString(), bIsNew); } /// /// Helper Function to read a file from _previousSeekPosition to End. /// /// contents of file starting from _previousSeekPosition private StringBuilder ReadFileContentsTillEndStartingFromPreviousSeekPosition() { lock (_lockingObj) { // Open the file FileStream fs = new FileStream(_FileNameNPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // Read File Contents into a StringBuilder StringBuilder sb = new StringBuilder(); bool bReadOrContinueReadingFile = true; while (bReadOrContinueReadingFile) { // Position the file _previousSeekPosition = (int)fs.Seek(_previousSeekPosition, SeekOrigin.Begin); //read from current seek position to end of file byte[] bytesRead = new byte[MAX_BYTE_TO_READ_IN_ONE_TIME]; int numBytes = fs.Read(bytesRead, 0, MAX_BYTE_TO_READ_IN_ONE_TIME); _previousSeekPosition += numBytes; // Append Data to the String Builder for (int i = 0; i < numBytes; i++) sb.Append((char)bytesRead[i]); // If we have read up to MAX_BYTE_TO_READ_IN_ONE_TIME, then there could be more data... bReadOrContinueReadingFile = (numBytes == MAX_BYTE_TO_READ_IN_ONE_TIME); } // Close File fs.Dispose(); fs = null; // Return SB object return sb; } } #endregion } }