using System.Buffers; using System.IO; using System.Net.Http; using System.Net; using System.Threading.Tasks; namespace Sandbox.Utility; /// /// Provides progress information for operations that process blocks of data, /// such as file uploads, downloads, or large data transfers. /// public struct DataProgress { /// /// The number of bytes processed so far. /// public long ProgressBytes { get; set; } /// /// The total number of bytes to process. /// public long TotalBytes { get; set; } /// /// The number of bytes processed since the last progress update. /// public long DeltaBytes { get; set; } /// /// Progress as a fraction from 0.0 to 1.0. /// public float ProgressDelta => (float)((double)ProgressBytes / (double)TotalBytes).Clamp( 0.0, 1.0 ); /// /// Callback delegate for receiving progress updates. /// public delegate void Callback( DataProgress progress ); /// /// HTTP content wrapper that reports upload progress through DataProgress callbacks. /// Used internally for tracking file upload progress. /// internal class HttpContentStream : HttpContent { private readonly Stream stream; private readonly int bufferSize; private readonly long length; /// /// Callback invoked as data is being uploaded. /// public Callback Progress { get; set; } /// /// Creates a new HTTP content stream with progress tracking. /// /// The source stream to upload /// Size of buffer for chunked uploads (default 256KB) public HttpContentStream( Stream stream, int bufferSize = 1024 * 256 ) { this.stream = stream; this.bufferSize = bufferSize; this.length = stream.Length; } protected override async Task SerializeToStreamAsync( Stream stream, TransportContext context ) { var buffer = new byte[bufferSize]; int bytesRead; long bytesTransferred = 0; while ( (bytesRead = await this.stream.ReadAsync( buffer, 0, buffer.Length )) > 0 ) { await stream.WriteAsync( buffer, 0, bytesRead ); bytesTransferred += bytesRead; Progress?.Invoke( new DataProgress { TotalBytes = length, ProgressBytes = bytesTransferred, DeltaBytes = bytesRead } ); } } protected override bool TryComputeLength( out long length ) { length = this.length; return true; } } }