2023-04-12 14:06:04 +00:00
|
|
|
|
using Logging;
|
2023-05-03 12:55:26 +00:00
|
|
|
|
using Utils;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
2023-09-11 08:43:27 +00:00
|
|
|
|
namespace FileUtils
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
|
|
|
|
public interface IFileManager
|
|
|
|
|
{
|
2023-09-13 08:03:11 +00:00
|
|
|
|
TrackedFile CreateEmptyFile(string label = "");
|
|
|
|
|
TrackedFile GenerateFile(ByteSize size, string label = "");
|
2024-07-01 13:59:08 +00:00
|
|
|
|
TrackedFile GenerateFile(Action<IGenerateOption> options, string label = "");
|
2023-09-13 08:03:11 +00:00
|
|
|
|
void DeleteAllFiles();
|
2023-09-11 08:43:27 +00:00
|
|
|
|
void ScopedFiles(Action action);
|
|
|
|
|
T ScopedFiles<T>(Func<T> action);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
public interface IGenerateOption
|
|
|
|
|
{
|
|
|
|
|
IGenerateOption Random(ByteSize size);
|
|
|
|
|
IGenerateOption StringRepeat(string str, ByteSize size);
|
|
|
|
|
IGenerateOption StringRepeat(string str, int times);
|
|
|
|
|
IGenerateOption ByteRepeat(byte[] bytes, ByteSize size);
|
|
|
|
|
IGenerateOption ByteRepeat(byte[] bytes, int times);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-12 14:06:04 +00:00
|
|
|
|
public class FileManager : IFileManager
|
|
|
|
|
{
|
2024-07-01 13:59:08 +00:00
|
|
|
|
private static readonly NumberSource folderNumberSource = new NumberSource(0);
|
2023-09-12 08:31:55 +00:00
|
|
|
|
private readonly ILog log;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
private readonly string folder;
|
2023-09-12 11:32:06 +00:00
|
|
|
|
private readonly List<List<TrackedFile>> fileSetStack = new List<List<TrackedFile>>();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
public const int ChunkSize = 1024 * 1024 * 100;
|
|
|
|
|
|
2023-09-12 08:31:55 +00:00
|
|
|
|
public FileManager(ILog log, string rootFolder)
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-09-11 08:43:27 +00:00
|
|
|
|
folder = Path.Combine(rootFolder, folderNumberSource.GetNextNumber().ToString("D5"));
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
|
|
|
|
this.log = log;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-13 08:03:11 +00:00
|
|
|
|
public TrackedFile CreateEmptyFile(string label = "")
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-09-21 08:33:09 +00:00
|
|
|
|
var path = Path.Combine(folder, Guid.NewGuid().ToString() + ".bin");
|
|
|
|
|
EnsureDirectory();
|
|
|
|
|
|
2023-09-12 11:32:06 +00:00
|
|
|
|
var result = new TrackedFile(log, path, label);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
File.Create(result.Filename).Close();
|
2023-05-29 07:13:38 +00:00
|
|
|
|
if (fileSetStack.Any()) fileSetStack.Last().Add(result);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
public TrackedFile GenerateFile(ByteSize size, string label = "")
|
|
|
|
|
{
|
|
|
|
|
return GenerateFile(o => o.Random(size), label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TrackedFile GenerateFile(Action<IGenerateOption> options, string label = "")
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-06-07 07:59:00 +00:00
|
|
|
|
var sw = Stopwatch.Begin(log);
|
2024-07-01 13:59:08 +00:00
|
|
|
|
var result = RunGenerators(options, label);
|
2024-03-22 12:36:33 +00:00
|
|
|
|
sw.End($"Generated file {result.Describe()}.");
|
2023-04-12 14:06:04 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-13 08:03:11 +00:00
|
|
|
|
public void DeleteAllFiles()
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-04-17 07:10:39 +00:00
|
|
|
|
DeleteDirectory();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-11 08:43:27 +00:00
|
|
|
|
public void ScopedFiles(Action action)
|
|
|
|
|
{
|
|
|
|
|
PushFileSet();
|
2024-04-01 18:40:03 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
action();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
PopFileSet();
|
|
|
|
|
}
|
2023-09-11 08:43:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T ScopedFiles<T>(Func<T> action)
|
|
|
|
|
{
|
|
|
|
|
PushFileSet();
|
2024-04-01 18:40:03 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return action();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
PopFileSet();
|
|
|
|
|
}
|
2023-09-11 08:43:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PushFileSet()
|
2023-05-29 07:13:38 +00:00
|
|
|
|
{
|
2023-09-12 11:32:06 +00:00
|
|
|
|
fileSetStack.Add(new List<TrackedFile>());
|
2023-05-29 07:13:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-11 08:43:27 +00:00
|
|
|
|
private void PopFileSet()
|
2023-05-29 07:13:38 +00:00
|
|
|
|
{
|
|
|
|
|
if (!fileSetStack.Any()) return;
|
|
|
|
|
var pop = fileSetStack.Last();
|
|
|
|
|
fileSetStack.Remove(pop);
|
|
|
|
|
|
|
|
|
|
foreach (var file in pop)
|
|
|
|
|
{
|
2023-09-21 08:33:09 +00:00
|
|
|
|
File.Delete(file.Filename);
|
2023-05-29 07:13:38 +00:00
|
|
|
|
}
|
2023-09-21 08:33:09 +00:00
|
|
|
|
|
|
|
|
|
// If the folder is now empty, delete it too.
|
|
|
|
|
if (!Directory.GetFiles(folder).Any()) DeleteDirectory();
|
2023-05-29 07:13:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
private TrackedFile RunGenerators(Action<IGenerateOption> options, string label)
|
2023-06-07 07:59:00 +00:00
|
|
|
|
{
|
2023-09-13 08:03:11 +00:00
|
|
|
|
var result = CreateEmptyFile(label);
|
2024-07-01 13:59:08 +00:00
|
|
|
|
var generators = GetGenerators(options);
|
|
|
|
|
CheckSpaceAvailable(result, generators.GetRequiredSpace());
|
2023-06-07 07:59:00 +00:00
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
using var stream = new FileStream(result.Filename, FileMode.Append);
|
|
|
|
|
generators.Run(stream);
|
2023-06-07 07:59:00 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
private GeneratorCollection GetGenerators(Action<IGenerateOption> options)
|
|
|
|
|
{
|
|
|
|
|
var result = new GeneratorCollection();
|
|
|
|
|
options(result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckSpaceAvailable(TrackedFile testFile, long requiredSize)
|
2023-06-07 07:59:00 +00:00
|
|
|
|
{
|
|
|
|
|
var file = new FileInfo(testFile.Filename);
|
|
|
|
|
var drive = new DriveInfo(file.Directory!.Root.FullName);
|
|
|
|
|
|
|
|
|
|
var spaceAvailable = drive.TotalFreeSpace;
|
|
|
|
|
|
2024-07-01 13:59:08 +00:00
|
|
|
|
if (spaceAvailable < requiredSize)
|
2023-06-07 07:59:00 +00:00
|
|
|
|
{
|
2023-09-20 08:51:47 +00:00
|
|
|
|
var msg = $"Not enough disk space. " +
|
2024-07-01 13:59:08 +00:00
|
|
|
|
$"{Formatter.FormatByteSize(requiredSize)} required. " +
|
2023-06-07 07:59:00 +00:00
|
|
|
|
$"{Formatter.FormatByteSize(spaceAvailable)} available.";
|
|
|
|
|
|
|
|
|
|
log.Log(msg);
|
2023-09-20 08:51:47 +00:00
|
|
|
|
throw new Exception(msg);
|
2023-06-07 07:59:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-17 07:10:39 +00:00
|
|
|
|
private void EnsureDirectory()
|
|
|
|
|
{
|
2023-09-21 08:33:09 +00:00
|
|
|
|
Directory.CreateDirectory(folder);
|
2023-04-17 07:10:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeleteDirectory()
|
|
|
|
|
{
|
2023-09-21 08:33:09 +00:00
|
|
|
|
if (Directory.Exists(folder)) Directory.Delete(folder, true);
|
2023-04-17 07:10:39 +00:00
|
|
|
|
}
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|