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