191 lines
7.0 KiB
C#
191 lines
7.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
|
|
namespace Yaulw.File
|
|
{
|
|
/// <remarks>
|
|
/// A File SystemWatcher utility that functions like the infamous unix tail utility,
|
|
/// with a callback when the file get's created or updated.
|
|
/// </remarks>
|
|
public class Tail
|
|
{
|
|
#region Public Events
|
|
|
|
/// <summary>
|
|
/// Event Delegate for the Incoming Data Event
|
|
/// </summary>
|
|
/// <param name="sender">object reference to the sending Tail Object</param>
|
|
/// <param name="newData">change data / or full data if bIsNewFile is true</param>
|
|
/// <param name="bIsNewFile">true if a new file was created</param>
|
|
public delegate void IncomingDataHandler(object sender, string newData, bool bIsNewFile);
|
|
|
|
/// <summary>
|
|
/// Caller must subscribe to the Incoming Data Event
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Construct a new Tail Object to monitor the specified file
|
|
/// </summary>
|
|
/// <param name="FileNameNPath">File to Monitor with Tail</param>
|
|
public Tail(string FileNameNPath)
|
|
{
|
|
_previousSeekPosition = 0;
|
|
_FileNameNPath = FileNameNPath;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Start Monitoring the File
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop Monitoring the File
|
|
/// </summary>
|
|
/// <param name="bDispose">set to false, if you are running into trouble during a shutdown</param>
|
|
public void StopMonitoring(bool bDispose = true)
|
|
{
|
|
_fileSystemWatcher.EnableRaisingEvents = false;
|
|
if(bDispose)
|
|
_fileSystemWatcher.Dispose();
|
|
_fileSystemWatcher = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>the Contents of the File</returns>
|
|
public StringBuilder ReadFromBeginningToEndOfFile()
|
|
{
|
|
_previousSeekPosition = 0;
|
|
return ReadFileContentsTillEndStartingFromPreviousSeekPosition();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Helper Methods
|
|
|
|
/// <summary>
|
|
/// Handled File System Watcher's Created Callback
|
|
/// </summary>
|
|
/// <param name="source">SystemWatcher object</param>
|
|
/// <param name="e">SystemWatcher event args</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handled File System Watcher's Changed Callback
|
|
/// </summary>
|
|
/// <param name="source">SystemWatcher object</param>
|
|
/// <param name="e">SystemWatcher event args</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper Function to read a file from _previousSeekPosition to End.
|
|
/// </summary>
|
|
/// <returns>contents of file starting from _previousSeekPosition</returns>
|
|
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
|
|
}
|
|
}
|