|
|
|
|
using System;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Security.Permissions;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace ZeroLevel.Services.FileSystem
|
|
|
|
|
{
|
|
|
|
|
public sealed class PeriodicFileSystemWatcher :
|
|
|
|
|
IDisposable
|
|
|
|
|
{
|
|
|
|
|
private long _updateSourceTaskKey;
|
|
|
|
|
private readonly string _sourceFolder;
|
|
|
|
|
private readonly string _temporaryFolder;
|
|
|
|
|
private readonly TimeSpan _period;
|
|
|
|
|
private readonly Func<FileMeta, Task> _callback;
|
|
|
|
|
|
|
|
|
|
public PeriodicFileSystemWatcher(TimeSpan period, string watch_folder, string temp_folder, Func<FileMeta, Task> callback)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(watch_folder))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(watch_folder));
|
|
|
|
|
}
|
|
|
|
|
if (callback == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(callback));
|
|
|
|
|
}
|
|
|
|
|
_callback = callback;
|
|
|
|
|
_sourceFolder = watch_folder;
|
|
|
|
|
_temporaryFolder = temp_folder;
|
|
|
|
|
_period = period;
|
|
|
|
|
if (_temporaryFolder.IndexOf(':') < 0)
|
|
|
|
|
{
|
|
|
|
|
_temporaryFolder = Path.Combine(Configuration.BaseDirectory, _temporaryFolder);
|
|
|
|
|
}
|
|
|
|
|
if (false == Directory.Exists(_temporaryFolder))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(_temporaryFolder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
|
|
|
|
_updateSourceTaskKey = Sheduller.RemindAsyncEvery(_period, CheckSourceFolder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
Sheduller.RemoveAsync(_updateSourceTaskKey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task CheckSourceFolder()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
foreach (var file in GetFilesFromSource())
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Log.Debug($"[PeriodicFileSystemWatcher] Find new file {file}");
|
|
|
|
|
if (FSUtils.IsFileLocked(new FileInfo(file)))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
string tempFile;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
tempFile = MoveToTemporary(file);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Failed to attempt to move file '{file}' to temporary directory '{_temporaryFolder}'");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Log.Debug($"[PeriodicFileSystemWatcher] Handle file {file}");
|
|
|
|
|
await _callback(new FileMeta(Path.GetFileName(file), tempFile));
|
|
|
|
|
File.Delete(tempFile);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Fault proceed file {file}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[PeriodicFileSystemWatcher] Failed to process input directory '{_sourceFolder}'");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Moving a file to a temporary directory
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string MoveToTemporary(string from)
|
|
|
|
|
{
|
|
|
|
|
if (from == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("from");
|
|
|
|
|
}
|
|
|
|
|
string tempFile = Path.Combine(_temporaryFolder, Path.GetFileName(from));
|
|
|
|
|
if (File.Exists(tempFile))
|
|
|
|
|
{
|
|
|
|
|
tempFile = TrySolveCollision(tempFile);
|
|
|
|
|
}
|
|
|
|
|
File.Copy(from, tempFile, false);
|
|
|
|
|
File.Delete(from);
|
|
|
|
|
return tempFile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Resolving collisions in filenames in the temporary directory
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static string TrySolveCollision(string file)
|
|
|
|
|
{
|
|
|
|
|
if (file == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("file");
|
|
|
|
|
}
|
|
|
|
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
|
|
|
|
|
string extension = Path.GetExtension(file);
|
|
|
|
|
string directoryName = Path.GetDirectoryName(file);
|
|
|
|
|
if (directoryName != null)
|
|
|
|
|
{
|
|
|
|
|
int num = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
num++;
|
|
|
|
|
}
|
|
|
|
|
while (File.Exists(Path.Combine(directoryName,
|
|
|
|
|
fileNameWithoutExtension + "_" +
|
|
|
|
|
num.ToString(CultureInfo.CurrentCulture) +
|
|
|
|
|
extension)));
|
|
|
|
|
return Path.Combine(directoryName,
|
|
|
|
|
fileNameWithoutExtension + "_" +
|
|
|
|
|
num.ToString(CultureInfo.CurrentCulture) +
|
|
|
|
|
extension);
|
|
|
|
|
}
|
|
|
|
|
throw new ArgumentException("folder");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Getting a list of files from the input directory
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string[] GetFilesFromSource()
|
|
|
|
|
{
|
|
|
|
|
string[] files = Directory.GetFiles(_sourceFolder, "*.*", SearchOption.TopDirectoryOnly);
|
|
|
|
|
Array.Sort<string>(files, FileNameSortCompare);
|
|
|
|
|
return files;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// File Name Comparison
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static int FileNameSortCompare(string x, string y)
|
|
|
|
|
{
|
|
|
|
|
int num;
|
|
|
|
|
int num2;
|
|
|
|
|
if (int.TryParse(Path.GetFileNameWithoutExtension(x), out num) &&
|
|
|
|
|
int.TryParse(Path.GetFileNameWithoutExtension(y), out num2))
|
|
|
|
|
{
|
|
|
|
|
return num - num2;
|
|
|
|
|
}
|
|
|
|
|
return string.Compare(x, y, StringComparison.InvariantCultureIgnoreCase);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
Sheduller.Remove(_updateSourceTaskKey);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|