using System.Buffers;
using StabilityMatrix.Core.Models.Progress;
namespace StabilityMatrix.Core.Helper;
public static class FileTransfers
{
///
/// Determines suitable buffer size based on stream length.
///
///
///
public static ulong GetBufferSize(ulong totalBytes) => totalBytes switch
{
< Size.MiB => 8 * Size.KiB,
< 100 * Size.MiB => 16 * Size.KiB,
< 500 * Size.MiB => Size.MiB,
< Size.GiB => 16 * Size.MiB,
_ => 32 * Size.MiB
};
public static async Task CopyFiles(Dictionary files, IProgress? fileProgress = default, IProgress? totalProgress = default)
{
var totalFiles = files.Count;
var currentFiles = 0;
var totalSize = Convert.ToUInt64(files.Keys.Select(x => new FileInfo(x).Length).Sum());
var totalRead = 0ul;
foreach(var (sourcePath, destPath) in files)
{
var totalReadForFile = 0ul;
await using var outStream = new FileStream(destPath, FileMode.Create, FileAccess.Write, FileShare.Read);
await using var inStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var fileSize = (ulong) inStream.Length;
var fileName = Path.GetFileName(sourcePath);
currentFiles++;
await CopyStream(inStream , outStream, fileReadBytes =>
{
var lastRead = totalReadForFile;
totalReadForFile = Convert.ToUInt64(fileReadBytes);
totalRead += totalReadForFile - lastRead;
fileProgress?.Report(new ProgressReport(totalReadForFile, fileSize, fileName, $"{currentFiles}/{totalFiles}"));
totalProgress?.Report(new ProgressReport(totalRead, totalSize, fileName, $"{currentFiles}/{totalFiles}"));
} );
}
}
private static async Task CopyStream(Stream from, Stream to, Action progress)
{
var shared = ArrayPool.Shared;
var bufferSize = (int) GetBufferSize((ulong) from.Length);
var buffer = shared.Rent(bufferSize);
var totalRead = 0L;
try
{
while (totalRead < from.Length)
{
var read = await from.ReadAsync(buffer.AsMemory(0, bufferSize));
await to.WriteAsync(buffer.AsMemory(0, read));
totalRead += read;
progress(totalRead);
}
}
finally
{
shared.Return(buffer);
}
}
}