mirror of
https://github.com/Grace-Solutions/PSMinIO.git
synced 2026-08-05 19:17:41 +00:00
Implement multiple concurrent chunk progress bars and intelligent size formatting
🚀 ENHANCED MULTIPART UPLOAD PROGRESS: ✅ MULTIPLE CONCURRENT CHUNK PROGRESS BARS: • Each chunk now has unique Activity ID (100 + partNumber) • Up to 900 concurrent chunks supported with separate progress bars • Parent progress bar shows overall file upload • Children progress bars show individual chunk uploads • Visual hierarchy: Collection → File → Individual Chunks 📊 INTELLIGENT SIZE FORMATTING: • Added FormatBytesIntelligent() method to SizeFormatter • Displays uploaded/total using same unit based on total file size • Example: '1.50 GB/4.57 GB' instead of '1536.00 MB/4.57 GB' Consistent unit formatting for better readability ENHANCED FILE PROGRESS DISPLAY: Shows: 'Uploading Win10ISO.zip - 1.50 GB/4.57 GB (Part 23/74) at 45.2 MB/s' Intelligent size units based on total file size Real-time uploaded bytes tracking Part progress and transfer speed included PROGRESS BAR HIERARCHY: Layer 1: Multipart Upload Collection (overall operation) Layer 2: File Upload (current file with uploaded/total bytes) Layer 3+: Individual Chunks (100+partNumber for unique IDs) EXPECTED VISUAL RESULT: Multipart Upload Collection: Processing Win10ISO.zip [] 50% File Upload: Uploading Win10ISO.zip - 2.28 GB/4.57 GB (Part 37/74) [] 50% Uploading Chunk: Part 35: 32.1 MB/64.0 MB [] 50% Uploading Chunk: Part 36: 45.2 MB/64.0 MB [] 70% Uploading Chunk: Part 37: 12.8 MB/64.0 MB [] 20% Uploading Chunk: Part 38: 8.4 MB/64.0 MB [] 13% Perfect visual feedback for concurrent multipart uploads!
This commit is contained in:
Binary file not shown.
@@ -60,7 +60,7 @@ namespace PSMinIO.Core.S3
|
||||
|
||||
// Layer 2: File Progress
|
||||
_progressCollector.QueueProgressUpdate(2, "File Upload",
|
||||
$"Uploading {fileInfo.Name} ({SizeFormatter.FormatBytes(totalSize)})", 0, 1);
|
||||
$"Uploading {fileInfo.Name} - {SizeFormatter.FormatBytesIntelligent(0, totalSize)}", 0, 1);
|
||||
|
||||
// Process initial progress updates
|
||||
_progressCollector.ProcessQueuedUpdates();
|
||||
@@ -161,9 +161,10 @@ namespace PSMinIO.Core.S3
|
||||
var elapsed = DateTime.UtcNow - operationStartTime;
|
||||
var speed = elapsed.TotalSeconds > 0 ? currentUploaded / elapsed.TotalSeconds : 0;
|
||||
|
||||
// Update file progress (Layer 2)
|
||||
// Update file progress (Layer 2) with intelligent size formatting
|
||||
var sizeDisplay = SizeFormatter.FormatBytesIntelligent(currentUploaded, totalSize);
|
||||
_progressCollector.QueueProgressUpdate(2, "File Upload",
|
||||
$"Part {partNum}/{totalParts} - {SizeFormatter.FormatBytes(currentUploaded)}/{SizeFormatter.FormatBytes(totalSize)} at {SizeFormatter.FormatSpeed(speed)}",
|
||||
$"Uploading {fileInfo.Name} - {sizeDisplay} (Part {partNum}/{totalParts}) at {SizeFormatter.FormatSpeed(speed)}",
|
||||
(int)fileProgress, 1);
|
||||
|
||||
// Log completion for larger chunks or milestone parts
|
||||
@@ -319,11 +320,13 @@ namespace PSMinIO.Core.S3
|
||||
fileStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create progress-tracking stream for chunk upload (Layer 3)
|
||||
// Use unique activity ID for each chunk: 100 + partNumber (allows up to 900 concurrent chunks)
|
||||
var chunkActivityId = 100 + partNumber;
|
||||
var progressStream = new ProgressTrackingStream(fileStream, size,
|
||||
(bytesRead, totalBytes) =>
|
||||
{
|
||||
var chunkProgress = (double)bytesRead / totalBytes * 100;
|
||||
_progressCollector.QueueProgressUpdate(3, "Uploading Chunk",
|
||||
_progressCollector.QueueProgressUpdate(chunkActivityId, "Uploading Chunk",
|
||||
$"Part {partNumber}: {SizeFormatter.FormatBytes(bytesRead)}/{SizeFormatter.FormatBytes(totalBytes)}",
|
||||
(int)chunkProgress, 2); // Parent: File Upload (Layer 2)
|
||||
});
|
||||
@@ -356,8 +359,8 @@ namespace PSMinIO.Core.S3
|
||||
|
||||
var etag = response.Headers.ETag?.Tag?.Trim('"') ?? "";
|
||||
|
||||
// Complete chunk progress (Layer 3)
|
||||
_progressCollector.QueueProgressCompletion(3, "Uploading Chunk", 2);
|
||||
// Complete chunk progress (Layer 3) - use same unique activity ID
|
||||
_progressCollector.QueueProgressCompletion(chunkActivityId, "Uploading Chunk", 2);
|
||||
|
||||
return new PartInfo
|
||||
{
|
||||
|
||||
@@ -160,5 +160,34 @@ namespace PSMinIO.Utils
|
||||
|
||||
return FormatDuration(TimeSpan.FromSeconds(remainingSeconds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats two byte values using the same unit based on the larger value
|
||||
/// Example: FormatBytesIntelligent(1536MB, 4.57GB) returns "1.50 GB/4.57 GB"
|
||||
/// </summary>
|
||||
/// <param name="currentBytes">Current byte count</param>
|
||||
/// <param name="totalBytes">Total byte count</param>
|
||||
/// <param name="decimalPlaces">Number of decimal places to show (default: 2)</param>
|
||||
/// <returns>Formatted size string with consistent units</returns>
|
||||
public static string FormatBytesIntelligent(long currentBytes, long totalBytes, int decimalPlaces = 2)
|
||||
{
|
||||
if (totalBytes == 0) return $"{FormatBytes(currentBytes, decimalPlaces)}/0 B";
|
||||
|
||||
// Determine the unit based on the total size (larger value)
|
||||
int unitIndex = 0;
|
||||
double totalSize = Math.Abs(totalBytes);
|
||||
|
||||
while (totalSize >= 1024 && unitIndex < SizeUnits.Length - 1)
|
||||
{
|
||||
totalSize /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
// Format both values using the same unit
|
||||
double currentSize = currentBytes / Math.Pow(1024, unitIndex);
|
||||
double totalSizeFormatted = totalBytes / Math.Pow(1024, unitIndex);
|
||||
|
||||
return $"{currentSize.ToString($"F{decimalPlaces}")} {SizeUnits[unitIndex]}/{totalSizeFormatted.ToString($"F{decimalPlaces}")} {SizeUnits[unitIndex]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user