Files
Yaulw/File/Tail.cs
2016-02-15 12:32:26 -05:00

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