diff --git a/Module/PSMinIO/bin/PSMinIO.dll b/Module/PSMinIO/bin/PSMinIO.dll index 9162723..22cdca6 100644 Binary files a/Module/PSMinIO/bin/PSMinIO.dll and b/Module/PSMinIO/bin/PSMinIO.dll differ diff --git a/Module/PSMinIO/bin/PSMinIO.pdb b/Module/PSMinIO/bin/PSMinIO.pdb index a458121..fcb7de1 100644 Binary files a/Module/PSMinIO/bin/PSMinIO.pdb and b/Module/PSMinIO/bin/PSMinIO.pdb differ diff --git a/src/Utils/ZipArchiveBuilder.cs b/src/Utils/ZipArchiveBuilder.cs index f61b97a..8e1b397 100644 --- a/src/Utils/ZipArchiveBuilder.cs +++ b/src/Utils/ZipArchiveBuilder.cs @@ -19,10 +19,10 @@ namespace PSMinIO.Utils private bool _disposed = false; // Performance optimization fields - private const long FlushThreshold = 5 * 1024 * 1024; // 5MB (reduced for more frequent flushing) + private const long FlushThreshold = 1 * 1024 * 1024; // 1MB (very frequent flushing for real-time updates) private long _bytesWrittenSinceFlush = 0; private DateTime _lastFlush = DateTime.UtcNow; - private const int FlushIntervalMs = 1000; // 1 second + private const int FlushIntervalMs = 500; // 500ms (very frequent time-based flushing) // Cancellation support private CancellationTokenSource? _cancellationTokenSource; @@ -110,12 +110,12 @@ namespace PSMinIO.Utils { try { - _archive?.Dispose(); // This forces the zip to be finalized + // Force flush the output stream first _outputStream?.Flush(); if (_outputStream is FileStream fileStream) { - fileStream.Flush(true); // Force OS flush + fileStream.Flush(true); // Force OS flush to disk } } catch @@ -124,6 +124,44 @@ namespace PSMinIO.Utils } } + /// + /// Aggressive flush that forces all buffers to disk + /// + private void AggressiveFlush() + { + try + { + // Try to flush the ZipArchive's internal buffers using reflection + var archiveType = _archive.GetType(); + var streamField = archiveType.GetField("_archiveStream", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (streamField?.GetValue(_archive) is Stream archiveStream) + { + archiveStream.Flush(); + } + + // Flush the output stream aggressively + _outputStream?.Flush(); + + if (_outputStream is FileStream fileStream) + { + fileStream.Flush(true); // Force OS-level flush + } + + // Force garbage collection to ensure all buffers are released + GC.Collect(0, GCCollectionMode.Optimized); + } + catch + { + // Ignore flush errors - fallback to basic flush + try + { + _outputStream?.Flush(); + if (_outputStream is FileStream fs) fs.Flush(true); + } + catch { } + } + } + /// /// Gets optimal buffer size based on file size /// @@ -249,7 +287,7 @@ namespace PSMinIO.Utils (now - _lastFlush).TotalMilliseconds > FlushIntervalMs) { entryStream.Flush(); - _outputStream.Flush(); // Also flush the underlying stream + AggressiveFlush(); // Force all data to disk immediately _bytesWrittenSinceFlush = 0; _lastFlush = now; } @@ -269,7 +307,7 @@ namespace PSMinIO.Utils // Final flush for this file entryStream.Flush(); - _outputStream.Flush(); // Force flush to disk + AggressiveFlush(); // Force all data to disk after each file } // Update metrics (access CompressedLength after the entry stream is closed) diff --git a/src/Utils/ZipBuilder.cs b/src/Utils/ZipBuilder.cs index 886c1bb..40ddcf0 100644 --- a/src/Utils/ZipBuilder.cs +++ b/src/Utils/ZipBuilder.cs @@ -204,12 +204,18 @@ namespace PSMinIO.Utils /// Zip creation result public ZipCreationResult CreateResult(string zipFilePath) { + // Ensure the archive is completed before creating result + _zipBuilder.Complete(); + + var endTime = _zipBuilder.EndTime ?? DateTime.UtcNow; + var duration = _zipBuilder.Duration ?? TimeSpan.Zero; + return new ZipCreationResult { ZipFilePath = zipFilePath, StartTime = _zipBuilder.StartTime, - EndTime = _zipBuilder.EndTime ?? DateTime.UtcNow, - Duration = _zipBuilder.Duration ?? TimeSpan.Zero, + EndTime = endTime, + Duration = duration, FileCount = _zipBuilder.FileCount, TotalUncompressedSize = _zipBuilder.TotalUncompressedSize, TotalCompressedSize = _zipBuilder.TotalCompressedSize,