using System;
using System.Collections.Concurrent;
using System.IO;
using System.Security.AccessControl;
using System.Threading;
using System.Threading.Tasks;
namespace ZeroLevel.Services.FileSystem
{
internal abstract class StoreTask
{
protected const int DEFAULT_STREAM_BUFFER_SIZE = 16384;
public async Task Store()
{
try
{
await StoreImpl().ConfigureAwait(false);
}
catch (Exception ex)
{
Log.SystemError(ex, "Fault store to archive");
}
}
protected static async Task TransferAsync(Stream input, Stream output)
{
if (input.CanRead == false)
{
throw new InvalidOperationException("Input stream can not be read.");
}
if (output.CanWrite == false)
{
throw new InvalidOperationException("Output stream can not be write.");
}
var readed = 0;
var buffer = new byte[DEFAULT_STREAM_BUFFER_SIZE];
while ((readed = await input.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) != 0)
{
output.Write(buffer, 0, readed);
}
output.Flush();
}
protected static Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
protected abstract Task StoreImpl();
}
internal sealed class StoreText :
StoreTask
{
private readonly string _text;
private readonly string _path;
public StoreText(string text, string path)
{
_text = text;
_path = path;
}
protected override async Task StoreImpl()
{
using (var input_stream = GenerateStreamFromString(_text))
{
using (var out_stream = File.Create(_path))
{
await TransferAsync(input_stream, out_stream).ConfigureAwait(false);
}
}
}
}
internal sealed class StoreData :
StoreTask
{
private readonly byte[] _data;
private readonly string _path;
public StoreData(byte[] data, string path)
{
_data = data;
_path = path;
}
protected override async Task StoreImpl()
{
using (var input_stream = new MemoryStream(_data))
{
using (var out_stream = File.Create(_path,
DEFAULT_STREAM_BUFFER_SIZE,
FileOptions.Asynchronous))
{
await TransferAsync(input_stream, out_stream).ConfigureAwait(false);
}
}
}
}
internal sealed class StoreStream :
StoreTask
{
private readonly Stream _stream;
private readonly string _path;
public StoreStream(Stream stream, string path)
{
_stream = stream;
_path = path;
}
protected override async Task StoreImpl()
{
using (_stream)
{
using (var out_stream = File.Create(_path,
DEFAULT_STREAM_BUFFER_SIZE,
FileOptions.Asynchronous))
{
await TransferAsync(_stream, out_stream).ConfigureAwait(false);
}
}
}
}
internal sealed class StoreFile :
StoreTask
{
private readonly string _input_path;
private readonly string _path;
public StoreFile(string input_file_path, string path)
{
_input_path = input_file_path;
_path = path;
}
protected override async Task StoreImpl()
{
using (var input_stream = File.Open(_input_path, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
using (var out_stream = File.Create(_path,
DEFAULT_STREAM_BUFFER_SIZE,
FileOptions.Asynchronous))
{
await TransferAsync(input_stream, out_stream).ConfigureAwait(false);
}
}
}
}
///
/// Файловый архив
///
public sealed class FileArchive :
IDisposable
{
private static int _counter;
private static string NextCounter
{ get { return Interlocked.Increment(ref _counter).ToString("X8"); } }
private readonly string _base_path;
private const string DIRECTORY_NAME_TEMPLATE = "yyyyMMdd";
private const string FILE_NAME_TEMPLATE = "HH_mm_ss_{0}{1}";
private readonly string _extension;
private readonly bool _override = false;
private readonly ConcurrentQueue _tasks = new ConcurrentQueue();
private volatile bool _disposed = false;
private volatile bool _stopped = false;
private string _currentArchivePath;
private bool _split_by_date = false;
public FileArchive(string base_path,
string ext,
bool split_by_date,
bool override_if_exists)
{
_base_path = PreparePath(base_path);
_extension = ext;
_override = override_if_exists;
_split_by_date = split_by_date;
RenewArchivePath();
if (_split_by_date)
{
Sheduller.RemindEveryNonlinearPeriod(
() => (DateTime.Now.AddDays(1).Date - DateTime.Now).Add(TimeSpan.FromMilliseconds(100)),
RenewArchivePath);
}
Task.Run(Consume);
}
private void RenewArchivePath()
{
if (_split_by_date)
{
var directory_name = DateTime.Now.ToString(DIRECTORY_NAME_TEMPLATE);
var directory_path = Path.Combine(_base_path, directory_name);
PrepareFolder(directory_path);
_currentArchivePath = directory_path;
}
else
{
var directory_path = _base_path;
PrepareFolder(directory_path);
_currentArchivePath = directory_path;
}
}
private async Task Consume()
{
do
{
StoreTask result;
while (_tasks.TryDequeue(out result))
{
await result.Store().ConfigureAwait(false);
}
Thread.Sleep(100);
} while (_disposed == false);
}
///
/// Save text to archive
///
/// Text
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void StoreText(string text, string subfolder_name = null, string file_name = null)
{
Apply(new StoreText(text, CreateArchiveFilePath(subfolder_name, file_name)));
}
///
/// Saving the specified file to the archive
///
/// File path
/// Archive file name (original file name by default)
///
public void Store(string file_path, string subfolder_name = null, string file_name = null)
{
Apply(new StoreFile(file_path, CreateArchiveFilePath(subfolder_name, file_name)));
}
public void Store(string file_path, bool immediate, string subfolder_name = null, string file_name = null)
{
if (immediate)
{
new StoreFile(file_path, CreateArchiveFilePath(subfolder_name, file_name)).Store().Wait();
}
else
{
Apply(new StoreFile(file_path, CreateArchiveFilePath(subfolder_name, file_name)));
}
}
///
/// Saving data from stream to archive
///
/// Data stream for reading
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void Store(Stream stream, string subfolder_name = null, string file_name = null)
{
Apply(new StoreStream(stream, CreateArchiveFilePath(subfolder_name, file_name)));
}
///
/// Saving data in binary form in the archive
///
/// Data
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void StoreData(byte[] data, string subfolder_name = null, string file_name = null)
{
Apply(new StoreData(data, CreateArchiveFilePath(subfolder_name, file_name)));
}
private void Apply(StoreTask task)
{
if (_stopped == false)
{
_tasks.Enqueue(task);
}
}
#region Helpers
private string CreateArchiveFilePath(string subfolder_name, string filename)
{
string archive_file_path;
if (string.IsNullOrWhiteSpace(subfolder_name))
{
if (string.IsNullOrWhiteSpace(filename))
{
do
{
archive_file_path = Path.Combine(_currentArchivePath,
string.Format(DateTime.Now.ToString(FILE_NAME_TEMPLATE),
NextCounter, _extension));
} while (_override == false && File.Exists(archive_file_path));
}
else
{
archive_file_path = Path.Combine(_currentArchivePath, filename);
}
}
else
{
var base_path = Path.Combine(_currentArchivePath, subfolder_name);
PrepareFolder(base_path);
if (string.IsNullOrWhiteSpace(filename))
{
do
{
archive_file_path = Path.Combine(base_path,
string.Format(DateTime.Now.ToString(FILE_NAME_TEMPLATE),
NextCounter, _extension));
} while (_override == false && File.Exists(archive_file_path));
}
else
{
archive_file_path = Path.Combine(base_path, filename);
}
}
return archive_file_path;
}
private static void PrepareFolder(string path)
{
if (Directory.Exists(path) == false)
{
Directory.CreateDirectory(path);
FSUtils.SetupFolderPermission(path,$"{Environment.UserDomainName}\\{Environment.UserName}",
FileSystemRights.Write | FileSystemRights.Read | FileSystemRights.Delete | FileSystemRights.Modify,
AccessControlType.Allow);
}
}
private static string PreparePath(string path)
{
if (path.IndexOf(':') == -1)
{
path = Path.Combine(Configuration.BaseDirectory, path);
}
return FSUtils.PathCorrection(path);
}
#endregion
public void Dispose()
{
_stopped = true;
while (_tasks.Count > 0)
{
Thread.Sleep(50);
}
_disposed = true;
}
}
public sealed class FileBuffer :
IDisposable
{
private readonly string _base_path;
private readonly BlockingCollection _tasks = new BlockingCollection();
private volatile bool _disposed = false;
private volatile bool _stopped = false;
private string _currentArchivePath;
private Thread _storeThread;
public FileBuffer(string base_path)
{
_base_path = PreparePath(base_path);
var directory_path = _base_path;
PrepareFolder(directory_path);
_currentArchivePath = directory_path;
_storeThread = new Thread(Consume);
_storeThread.IsBackground = true;
_storeThread.Start();
}
private void Consume()
{
StoreTask result;
while (_disposed == false)
{
result = _tasks.Take();
result.Store().ContinueWith(t =>
{
if (t.IsFaulted)
{
Log.SystemError(t.Exception, "[FileBuffer] Fault store file");
}
});
}
}
///
/// Save text to archive
///
/// Text
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void StoreText(string text, string name = null)
{
Apply(new StoreText(text, CreateArchiveFilePath(name)));
}
///
/// Saving the specified file to the archive
///
/// File path
/// Archive file name (original file name by default)
///
public void Store(string file_path, string name = null)
{
Apply(new StoreFile(file_path, CreateArchiveFilePath(name)));
}
///
/// Sync saving the specified file to the archive
///
/// File path
/// Archive file name (original file name by default)
///
public void Store(string file_path, bool immediate, string name = null)
{
if (immediate)
{
new StoreFile(file_path, CreateArchiveFilePath(name)).Store().Wait();
}
else
{
Apply(new StoreFile(file_path, CreateArchiveFilePath(name)));
}
}
///
/// Saving data from stream to archive
///
/// Data stream for reading
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void Store(Stream stream, string name = null)
{
Apply(new StoreStream(stream, CreateArchiveFilePath(name)));
}
///
/// Сохранение данных в бинарном виде в архив
///
/// Data
/// Archive file name (HH_mm_ss_fff_counter.{ext} by default)
///
public void StoreData(byte[] data, string name = null)
{
Apply(new StoreData(data, CreateArchiveFilePath(name)));
}
private void Apply(StoreTask task)
{
if (_stopped == false)
{
_tasks.Add(task);
}
}
#region Helpers
private string CreateArchiveFilePath(string original_name)
{
return Path.Combine(_currentArchivePath, FSUtils.FileNameCorrection(original_name));
}
private static void PrepareFolder(string path)
{
if (Directory.Exists(path) == false)
{
Directory.CreateDirectory(path);
FSUtils.SetupFolderPermission(path,
$"{Environment.UserDomainName}\\{Environment.UserName}",
FileSystemRights.Write | FileSystemRights.Read | FileSystemRights.Delete | FileSystemRights.Modify,
AccessControlType.Allow);
}
}
private static string PreparePath(string path)
{
if (path.IndexOf(':') == -1)
{
path = Path.Combine(Configuration.BaseDirectory, path);
}
return FSUtils.PathCorrection(path);
}
#endregion
public void Dispose()
{
_stopped = true;
_tasks.CompleteAdding();
while (_tasks.Count > 0)
{
Thread.Sleep(50);
}
_disposed = true;
}
}
}