mirror of
https://github.com/Grace-Solutions/PSMinIO.git
synced 2026-09-04 08:05:25 +00:00
Add Get-MinIOZipArchive cmdlet and enhance New-MinIOZipArchive with FileInfo parameters
✅ Get-MinIOZipArchive cmdlet - Comprehensive zip reading: • FileInfo parameter with proper disposal handling using OpenRead • ZipArchiveInfo result with comprehensive metrics • ZipEntryInfo array for detailed entry information • IncludeEntries parameter for detailed file listings • ValidateIntegrity parameter for archive validation • Filter parameter for entry name pattern matching • Proper exception handling and verbose logging • Always returns objects for pipeline compatibility ✅ Enhanced New-MinIOZipArchive cmdlet: • FileInfo DestinationPath parameter (was string) • Removed PassThru parameter - always returns objects • Consistent parameter naming with aliases • Improved error handling and validation • Better integration with PowerShell pipeline ✅ ZipArchiveInfo and ZipEntryInfo classes: • Comprehensive metrics: size, compression ratio, efficiency • Time tracking: creation, modification, validation duration • Entry details: full name, size, compression stats • Directory detection and space saved calculations • .NET Standard 2.0 compatibility (removed Crc32) ✅ Enhanced test script: • FileInfo parameter usage examples • Get-MinIOZipArchive functionality testing • Archive information reading and validation • Detailed entry inspection and filtering • Integrity validation demonstrations ✅ Architecture improvements: • Consistent FileInfo usage across zip operations • Proper disposal handling with using statements • Thread-safe operations with comprehensive error handling • Pipeline-friendly object returns • Enhanced verbose logging and progress tracking Features: Create zips from FileInfo/DirectoryInfo, read archive info with validation, detailed entry inspection, integrity checking, and comprehensive metrics.
This commit is contained in:
@@ -71,6 +71,7 @@
|
|||||||
'Get-MinIOObject',
|
'Get-MinIOObject',
|
||||||
'New-MinIOObject',
|
'New-MinIOObject',
|
||||||
'Get-MinIOObjectContent',
|
'Get-MinIOObjectContent',
|
||||||
|
'Get-MinIOZipArchive',
|
||||||
'New-MinIOZipArchive'
|
'New-MinIOZipArchive'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+62
-34
@@ -53,10 +53,10 @@ try {
|
|||||||
# Test 1: Create zip from FileInfo array
|
# Test 1: Create zip from FileInfo array
|
||||||
Write-Host "`n3. Test 1: Creating zip from FileInfo array..." -ForegroundColor Yellow
|
Write-Host "`n3. Test 1: Creating zip from FileInfo array..." -ForegroundColor Yellow
|
||||||
$mainFiles = Get-ChildItem $TestDirectory -File
|
$mainFiles = Get-ChildItem $TestDirectory -File
|
||||||
$zipPath1 = "test-files-array.zip"
|
$zipFile1 = [System.IO.FileInfo]"test-files-array.zip"
|
||||||
|
|
||||||
Write-Host " Creating zip with $($mainFiles.Count) files using Files parameter set" -ForegroundColor Cyan
|
Write-Host " Creating zip with $($mainFiles.Count) files using Files parameter set" -ForegroundColor Cyan
|
||||||
$result1 = New-MinIOZipArchive -DestinationPath $zipPath1 -Path $mainFiles -CompressionLevel Optimal -PassThru -Verbose
|
$result1 = New-MinIOZipArchive -DestinationPath $zipFile1 -Path $mainFiles -CompressionLevel Optimal -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Files array zip created:" -ForegroundColor Green
|
Write-Host "✅ Files array zip created:" -ForegroundColor Green
|
||||||
Write-Host " Files: $($result1.FileCount)" -ForegroundColor White
|
Write-Host " Files: $($result1.FileCount)" -ForegroundColor White
|
||||||
@@ -67,10 +67,10 @@ try {
|
|||||||
|
|
||||||
# Test 2: Create zip from directory (non-recursive)
|
# Test 2: Create zip from directory (non-recursive)
|
||||||
Write-Host "`n4. Test 2: Creating zip from directory (non-recursive)..." -ForegroundColor Yellow
|
Write-Host "`n4. Test 2: Creating zip from directory (non-recursive)..." -ForegroundColor Yellow
|
||||||
$zipPath2 = "test-directory-flat.zip"
|
$zipFile2 = [System.IO.FileInfo]"test-directory-flat.zip"
|
||||||
|
|
||||||
Write-Host " Creating zip from directory (top-level files only)" -ForegroundColor Cyan
|
Write-Host " Creating zip from directory (top-level files only)" -ForegroundColor Cyan
|
||||||
$result2 = New-MinIOZipArchive -DestinationPath $zipPath2 -Directory (Get-Item $TestDirectory) -CompressionLevel Fastest -PassThru -Verbose
|
$result2 = New-MinIOZipArchive -DestinationPath $zipFile2 -Directory (Get-Item $TestDirectory) -CompressionLevel Fastest -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Directory flat zip created:" -ForegroundColor Green
|
Write-Host "✅ Directory flat zip created:" -ForegroundColor Green
|
||||||
Write-Host " Files: $($result2.FileCount)" -ForegroundColor White
|
Write-Host " Files: $($result2.FileCount)" -ForegroundColor White
|
||||||
@@ -78,10 +78,10 @@ try {
|
|||||||
|
|
||||||
# Test 3: Create zip from directory (recursive)
|
# Test 3: Create zip from directory (recursive)
|
||||||
Write-Host "`n5. Test 3: Creating zip from directory (recursive)..." -ForegroundColor Yellow
|
Write-Host "`n5. Test 3: Creating zip from directory (recursive)..." -ForegroundColor Yellow
|
||||||
$zipPath3 = "test-directory-recursive.zip"
|
$zipFile3 = [System.IO.FileInfo]"test-directory-recursive.zip"
|
||||||
|
|
||||||
Write-Host " Creating zip from directory (recursive, all subdirectories)" -ForegroundColor Cyan
|
Write-Host " Creating zip from directory (recursive, all subdirectories)" -ForegroundColor Cyan
|
||||||
$result3 = New-MinIOZipArchive -DestinationPath $zipPath3 -Directory (Get-Item $TestDirectory) -Recursive -IncludeBaseDirectory -CompressionLevel Optimal -PassThru -Verbose
|
$result3 = New-MinIOZipArchive -DestinationPath $zipFile3 -Directory (Get-Item $TestDirectory) -Recursive -IncludeBaseDirectory -CompressionLevel Optimal -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Directory recursive zip created:" -ForegroundColor Green
|
Write-Host "✅ Directory recursive zip created:" -ForegroundColor Green
|
||||||
Write-Host " Files: $($result3.FileCount)" -ForegroundColor White
|
Write-Host " Files: $($result3.FileCount)" -ForegroundColor White
|
||||||
@@ -89,55 +89,76 @@ try {
|
|||||||
|
|
||||||
# Test 4: Create zip with file filtering
|
# Test 4: Create zip with file filtering
|
||||||
Write-Host "`n6. Test 4: Creating zip with file filtering..." -ForegroundColor Yellow
|
Write-Host "`n6. Test 4: Creating zip with file filtering..." -ForegroundColor Yellow
|
||||||
$zipPath4 = "test-filtered-logs.zip"
|
$zipFile4 = [System.IO.FileInfo]"test-filtered-logs.zip"
|
||||||
|
|
||||||
Write-Host " Creating zip with only .log files using InclusionFilter" -ForegroundColor Cyan
|
Write-Host " Creating zip with only .log files using InclusionFilter" -ForegroundColor Cyan
|
||||||
$result4 = New-MinIOZipArchive -DestinationPath $zipPath4 -Directory (Get-Item $TestDirectory) -Recursive -InclusionFilter { $_.Extension -eq ".log" } -CompressionLevel Optimal -PassThru -Verbose
|
$result4 = New-MinIOZipArchive -DestinationPath $zipFile4 -Directory (Get-Item $TestDirectory) -Recursive -InclusionFilter { $_.Extension -eq ".log" } -CompressionLevel Optimal -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Filtered zip created:" -ForegroundColor Green
|
Write-Host "✅ Filtered zip created:" -ForegroundColor Green
|
||||||
Write-Host " Log files: $($result4.FileCount)" -ForegroundColor White
|
Write-Host " Log files: $($result4.FileCount)" -ForegroundColor White
|
||||||
Write-Host " Compression: $($result4.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White
|
Write-Host " Compression: $($result4.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White
|
||||||
|
|
||||||
# Test 5: Append to existing zip (Update mode)
|
# Test 5: Append to existing zip (Update mode)
|
||||||
Write-Host "`n7. Test 5: Appending to existing zip..." -ForegroundColor Yellow
|
Write-Host "`n7. Test 5: Appending to existing zip..." -ForegroundColor Yellow
|
||||||
$csvFiles = Get-ChildItem $TestDirectory -Recurse -Filter "*.csv"
|
$csvFiles = Get-ChildItem $TestDirectory -Recurse -Filter "*.csv"
|
||||||
|
|
||||||
Write-Host " Appending $($csvFiles.Count) CSV files to existing zip" -ForegroundColor Cyan
|
Write-Host " Appending $($csvFiles.Count) CSV files to existing zip" -ForegroundColor Cyan
|
||||||
$result5 = New-MinIOZipArchive -DestinationPath $zipPath4 -Path $csvFiles -Mode Update -CompressionLevel Optimal -PassThru -Verbose
|
$result5 = New-MinIOZipArchive -DestinationPath $zipFile4 -Path $csvFiles -Mode Update -CompressionLevel Optimal -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Files appended to zip:" -ForegroundColor Green
|
Write-Host "✅ Files appended to zip:" -ForegroundColor Green
|
||||||
Write-Host " Total files now: $($result5.FileCount)" -ForegroundColor White
|
Write-Host " Total files now: $($result5.FileCount)" -ForegroundColor White
|
||||||
|
|
||||||
# Test 6: Create zip with custom base path
|
# Test 6: Create zip with custom base path
|
||||||
Write-Host "`n8. Test 6: Creating zip with custom base path..." -ForegroundColor Yellow
|
Write-Host "`n8. Test 6: Creating zip with custom base path..." -ForegroundColor Yellow
|
||||||
$zipPath6 = "test-custom-basepath.zip"
|
$zipFile6 = [System.IO.FileInfo]"test-custom-basepath.zip"
|
||||||
|
|
||||||
Write-Host " Creating zip with custom base path (flattened structure)" -ForegroundColor Cyan
|
Write-Host " Creating zip with custom base path (flattened structure)" -ForegroundColor Cyan
|
||||||
$result6 = New-MinIOZipArchive -DestinationPath $zipPath6 -Directory (Get-Item $TestDirectory) -Recursive -BasePath $TestDirectory -CompressionLevel Optimal -PassThru -Verbose
|
$result6 = New-MinIOZipArchive -DestinationPath $zipFile6 -Directory (Get-Item $TestDirectory) -Recursive -BasePath $TestDirectory -CompressionLevel Optimal -Verbose
|
||||||
|
|
||||||
Write-Host "✅ Custom base path zip created:" -ForegroundColor Green
|
Write-Host "✅ Custom base path zip created:" -ForegroundColor Green
|
||||||
Write-Host " Files: $($result6.FileCount)" -ForegroundColor White
|
Write-Host " Files: $($result6.FileCount)" -ForegroundColor White
|
||||||
|
|
||||||
# Verify all zip files
|
# Test 7: Get zip archive information
|
||||||
Write-Host "`n9. Verifying created zip files..." -ForegroundColor Yellow
|
Write-Host "`n9. Test 7: Reading zip archive information..." -ForegroundColor Yellow
|
||||||
$zipFiles = Get-ChildItem "*.zip"
|
$zipFiles = Get-ChildItem "*.zip"
|
||||||
|
|
||||||
foreach ($zipFile in $zipFiles) {
|
foreach ($zipFile in $zipFiles) {
|
||||||
try {
|
Write-Host " Reading archive: $($zipFile.Name)" -ForegroundColor Cyan
|
||||||
$archive = [System.IO.Compression.ZipFile]::OpenRead($zipFile.FullName)
|
|
||||||
$entryCount = $archive.Entries.Count
|
# Basic archive info
|
||||||
$archive.Dispose()
|
$archiveInfo = Get-MinIOZipArchive -ZipFile $zipFile -Verbose
|
||||||
Write-Host " ✅ $($zipFile.Name): $entryCount entries" -ForegroundColor Green
|
Write-Host " Entries: $($archiveInfo.EntryCount)" -ForegroundColor White
|
||||||
|
Write-Host " Size: $([math]::Round($archiveInfo.TotalUncompressedSize / 1KB, 2)) KB -> $([math]::Round($archiveInfo.TotalCompressedSize / 1KB, 2)) KB" -ForegroundColor White
|
||||||
|
Write-Host " Compression: $($archiveInfo.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White
|
||||||
|
|
||||||
|
# Detailed entries for one archive
|
||||||
|
if ($zipFile.Name -eq "test-directory-recursive.zip") {
|
||||||
|
Write-Host " Getting detailed entries for recursive archive..." -ForegroundColor Cyan
|
||||||
|
$detailedInfo = Get-MinIOZipArchive -ZipFile $zipFile -IncludeEntries -Verbose
|
||||||
|
Write-Host " Entry details: $($detailedInfo.Entries.Count) entries" -ForegroundColor White
|
||||||
|
|
||||||
|
# Show first few entries
|
||||||
|
$detailedInfo.Entries | Select-Object -First 3 | ForEach-Object {
|
||||||
|
Write-Host " $($_.FullName) ($([math]::Round($_.Length / 1KB, 2)) KB)" -ForegroundColor Gray
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
|
||||||
Write-Host " ❌ $($zipFile.Name): Verification failed - $($_.Exception.Message)" -ForegroundColor Red
|
# Validate integrity for one archive
|
||||||
|
if ($zipFile.Name -eq "test-files-array.zip") {
|
||||||
|
Write-Host " Validating archive integrity..." -ForegroundColor Cyan
|
||||||
|
$validatedInfo = Get-MinIOZipArchive -ZipFile $zipFile -ValidateIntegrity -Verbose
|
||||||
|
$validStatus = if ($validatedInfo.IsValid) { "✅ Valid" } else { "❌ Invalid" }
|
||||||
|
Write-Host " Validation: $validStatus (took $($validatedInfo.ValidationDuration.TotalMilliseconds.ToString('F0'))ms)" -ForegroundColor White
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Host "✅ Archive reading tests completed!" -ForegroundColor Green
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
Write-Host "`n=== TEST SUMMARY ===" -ForegroundColor Cyan
|
Write-Host "`n=== TEST SUMMARY ===" -ForegroundColor Cyan
|
||||||
Write-Host "✅ All zip archive tests completed successfully!" -ForegroundColor Green
|
Write-Host "✅ All zip archive tests completed successfully!" -ForegroundColor Green
|
||||||
Write-Host "Features tested:" -ForegroundColor Yellow
|
Write-Host "New-MinIOZipArchive features tested:" -ForegroundColor Yellow
|
||||||
|
Write-Host " • FileInfo destination path parameter" -ForegroundColor White
|
||||||
Write-Host " • FileInfo[] parameter with Files parameter set" -ForegroundColor White
|
Write-Host " • FileInfo[] parameter with Files parameter set" -ForegroundColor White
|
||||||
Write-Host " • Directory parameter with recursive and non-recursive modes" -ForegroundColor White
|
Write-Host " • Directory parameter with recursive and non-recursive modes" -ForegroundColor White
|
||||||
Write-Host " • File filtering with InclusionFilter ScriptBlock" -ForegroundColor White
|
Write-Host " • File filtering with InclusionFilter ScriptBlock" -ForegroundColor White
|
||||||
@@ -145,7 +166,14 @@ try {
|
|||||||
Write-Host " • Custom base path for entry name control" -ForegroundColor White
|
Write-Host " • Custom base path for entry name control" -ForegroundColor White
|
||||||
Write-Host " • Multiple compression levels (Optimal, Fastest)" -ForegroundColor White
|
Write-Host " • Multiple compression levels (Optimal, Fastest)" -ForegroundColor White
|
||||||
Write-Host " • Comprehensive progress tracking and metrics" -ForegroundColor White
|
Write-Host " • Comprehensive progress tracking and metrics" -ForegroundColor White
|
||||||
Write-Host " • PassThru parameter for detailed results" -ForegroundColor White
|
Write-Host " • Always returns result objects (no PassThru needed)" -ForegroundColor White
|
||||||
|
|
||||||
|
Write-Host "Get-MinIOZipArchive features tested:" -ForegroundColor Yellow
|
||||||
|
Write-Host " • Basic archive information reading" -ForegroundColor White
|
||||||
|
Write-Host " • Detailed entry information with IncludeEntries" -ForegroundColor White
|
||||||
|
Write-Host " • Archive integrity validation" -ForegroundColor White
|
||||||
|
Write-Host " • Proper disposal handling with OpenRead" -ForegroundColor White
|
||||||
|
Write-Host " • Comprehensive metrics and compression statistics" -ForegroundColor White
|
||||||
|
|
||||||
Write-Host "`nZip files created:" -ForegroundColor Yellow
|
Write-Host "`nZip files created:" -ForegroundColor Yellow
|
||||||
foreach ($zipFile in $zipFiles) {
|
foreach ($zipFile in $zipFiles) {
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSMinIO.Core.Models;
|
||||||
|
using PSMinIO.Utils;
|
||||||
|
|
||||||
|
namespace PSMinIO.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opens and reads zip archive information with proper disposal handling
|
||||||
|
/// </summary>
|
||||||
|
[Cmdlet(VerbsCommon.Get, "MinIOZipArchive")]
|
||||||
|
[OutputType(typeof(ZipArchiveInfo))]
|
||||||
|
public class GetMinIOZipArchiveCmdlet : MinIOBaseCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Path to the zip archive file to read
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
|
||||||
|
[ValidateNotNull]
|
||||||
|
[Alias("ZipPath", "Archive", "FullName")]
|
||||||
|
public FileInfo ZipFile { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Include detailed entry information for each file in the archive
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public SwitchParameter IncludeEntries { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate the archive integrity by attempting to read all entries
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public SwitchParameter ValidateIntegrity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filter entries by name pattern (supports wildcards)
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
[ValidateNotNullOrEmpty]
|
||||||
|
public string? Filter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
// Validate zip file exists
|
||||||
|
if (!ZipFile.Exists)
|
||||||
|
{
|
||||||
|
var errorRecord = new ErrorRecord(
|
||||||
|
new FileNotFoundException($"Zip file not found: {ZipFile.FullName}"),
|
||||||
|
"ZipFileNotFound",
|
||||||
|
ErrorCategory.ObjectNotFound,
|
||||||
|
ZipFile);
|
||||||
|
ThrowTerminatingError(errorRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
var archiveInfo = ExecuteOperation("ReadZipArchive", () =>
|
||||||
|
{
|
||||||
|
WriteVerboseMessage("Reading zip archive: {0}", ZipFile.FullName);
|
||||||
|
|
||||||
|
ZipArchiveInfo result;
|
||||||
|
using (var fileStream = ZipFile.OpenRead())
|
||||||
|
using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Read))
|
||||||
|
{
|
||||||
|
// Create archive info
|
||||||
|
result = new ZipArchiveInfo
|
||||||
|
{
|
||||||
|
ZipFilePath = ZipFile.FullName,
|
||||||
|
ZipFileName = ZipFile.Name,
|
||||||
|
ZipFileSize = ZipFile.Length,
|
||||||
|
CreationTime = ZipFile.CreationTime,
|
||||||
|
LastWriteTime = ZipFile.LastWriteTime,
|
||||||
|
EntryCount = archive.Entries.Count
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate total uncompressed size
|
||||||
|
result.TotalUncompressedSize = archive.Entries.Sum(e => e.Length);
|
||||||
|
result.TotalCompressedSize = archive.Entries.Sum(e => e.CompressedLength);
|
||||||
|
result.CompressionRatio = result.TotalUncompressedSize > 0
|
||||||
|
? (double)result.TotalCompressedSize / result.TotalUncompressedSize
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
WriteVerboseMessage("Archive contains {0} entries, {1} -> {2} ({3:F1}% compression)",
|
||||||
|
result.EntryCount,
|
||||||
|
SizeFormatter.FormatBytes(result.TotalUncompressedSize),
|
||||||
|
SizeFormatter.FormatBytes(result.TotalCompressedSize),
|
||||||
|
(1 - result.CompressionRatio) * 100);
|
||||||
|
|
||||||
|
// Include detailed entries if requested
|
||||||
|
if (IncludeEntries.IsPresent)
|
||||||
|
{
|
||||||
|
var entries = archive.Entries.AsEnumerable();
|
||||||
|
|
||||||
|
// Apply filter if specified
|
||||||
|
if (!string.IsNullOrEmpty(Filter))
|
||||||
|
{
|
||||||
|
var wildcardPattern = new WildcardPattern(Filter, WildcardOptions.IgnoreCase);
|
||||||
|
entries = entries.Where(e => wildcardPattern.IsMatch(e.FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Entries = entries.Select(entry => new ZipEntryInfo
|
||||||
|
{
|
||||||
|
FullName = entry.FullName,
|
||||||
|
Name = entry.Name,
|
||||||
|
Length = entry.Length,
|
||||||
|
CompressedLength = entry.CompressedLength,
|
||||||
|
CompressionRatio = entry.Length > 0 ? (double)entry.CompressedLength / entry.Length : 0,
|
||||||
|
LastWriteTime = entry.LastWriteTime,
|
||||||
|
IsDirectory = string.IsNullOrEmpty(entry.Name) && entry.FullName.EndsWith("/")
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
WriteVerboseMessage("Included {0} entry details", result.Entries.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate integrity if requested
|
||||||
|
if (ValidateIntegrity.IsPresent)
|
||||||
|
{
|
||||||
|
WriteVerboseMessage("Validating archive integrity...");
|
||||||
|
var validationStart = DateTime.UtcNow;
|
||||||
|
var validEntries = 0;
|
||||||
|
var invalidEntries = 0;
|
||||||
|
|
||||||
|
foreach (var entry in archive.Entries)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var entryStream = entry.Open())
|
||||||
|
{
|
||||||
|
// Read a small portion to validate the entry can be opened
|
||||||
|
var buffer = new byte[1024];
|
||||||
|
entryStream.Read(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
validEntries++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
invalidEntries++;
|
||||||
|
WriteWarning($"Entry '{entry.FullName}' failed validation: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validationDuration = DateTime.UtcNow - validationStart;
|
||||||
|
result.IsValid = invalidEntries == 0;
|
||||||
|
result.ValidationDuration = validationDuration;
|
||||||
|
|
||||||
|
WriteVerboseMessage("Validation completed in {0}: {1} valid, {2} invalid entries",
|
||||||
|
SizeFormatter.FormatDuration(validationDuration),
|
||||||
|
validEntries,
|
||||||
|
invalidEntries);
|
||||||
|
|
||||||
|
if (invalidEntries > 0)
|
||||||
|
{
|
||||||
|
WriteWarning($"Archive validation found {invalidEntries} corrupted entries");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteVerboseMessage("Successfully read zip archive information");
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}, $"ZipFile: {ZipFile.FullName}");
|
||||||
|
|
||||||
|
// Always return the archive info object
|
||||||
|
WriteObject(archiveInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Information about a zip archive
|
||||||
|
/// </summary>
|
||||||
|
public class ZipArchiveInfo
|
||||||
|
{
|
||||||
|
public string ZipFilePath { get; set; } = string.Empty;
|
||||||
|
public string ZipFileName { get; set; } = string.Empty;
|
||||||
|
public long ZipFileSize { get; set; }
|
||||||
|
public DateTime CreationTime { get; set; }
|
||||||
|
public DateTime LastWriteTime { get; set; }
|
||||||
|
public int EntryCount { get; set; }
|
||||||
|
public long TotalUncompressedSize { get; set; }
|
||||||
|
public long TotalCompressedSize { get; set; }
|
||||||
|
public double CompressionRatio { get; set; }
|
||||||
|
public double CompressionEfficiency => (1 - CompressionRatio) * 100;
|
||||||
|
public long SpaceSaved => TotalUncompressedSize - TotalCompressedSize;
|
||||||
|
public ZipEntryInfo[]? Entries { get; set; }
|
||||||
|
public bool? IsValid { get; set; }
|
||||||
|
public TimeSpan? ValidationDuration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Information about a zip archive entry
|
||||||
|
/// </summary>
|
||||||
|
public class ZipEntryInfo
|
||||||
|
{
|
||||||
|
public string FullName { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public long Length { get; set; }
|
||||||
|
public long CompressedLength { get; set; }
|
||||||
|
public double CompressionRatio { get; set; }
|
||||||
|
public double CompressionEfficiency => (1 - CompressionRatio) * 100;
|
||||||
|
public long SpaceSaved => Length - CompressedLength;
|
||||||
|
public DateTimeOffset LastWriteTime { get; set; }
|
||||||
|
public bool IsDirectory { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,12 +17,12 @@ namespace PSMinIO.Cmdlets
|
|||||||
public class NewMinIOZipArchiveCmdlet : MinIOBaseCmdlet
|
public class NewMinIOZipArchiveCmdlet : MinIOBaseCmdlet
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path where the zip archive will be created
|
/// FileInfo object representing where the zip archive will be created
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter(Position = 0, Mandatory = true)]
|
[Parameter(Position = 0, Mandatory = true)]
|
||||||
[ValidateNotNullOrEmpty]
|
[ValidateNotNull]
|
||||||
[Alias("ZipPath", "Archive")]
|
[Alias("ZipPath", "Archive", "Destination")]
|
||||||
public string DestinationPath { get; set; } = string.Empty;
|
public FileInfo DestinationPath { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Array of FileInfo objects to add to the zip
|
/// Array of FileInfo objects to add to the zip
|
||||||
@@ -99,23 +99,16 @@ namespace PSMinIO.Cmdlets
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public SwitchParameter Force { get; set; }
|
public SwitchParameter Force { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the zip creation result
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public SwitchParameter PassThru { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the cmdlet
|
/// Processes the cmdlet
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void ProcessRecord()
|
protected override void ProcessRecord()
|
||||||
{
|
{
|
||||||
// Validate destination path
|
// Validate destination path
|
||||||
var destinationInfo = new FileInfo(DestinationPath);
|
if (DestinationPath.Exists && !Force.IsPresent)
|
||||||
if (destinationInfo.Exists && !Force.IsPresent)
|
|
||||||
{
|
{
|
||||||
var errorRecord = new ErrorRecord(
|
var errorRecord = new ErrorRecord(
|
||||||
new InvalidOperationException($"Zip file already exists: {DestinationPath}. Use -Force to overwrite."),
|
new InvalidOperationException($"Zip file already exists: {DestinationPath.FullName}. Use -Force to overwrite."),
|
||||||
"ZipFileExists",
|
"ZipFileExists",
|
||||||
ErrorCategory.ResourceExists,
|
ErrorCategory.ResourceExists,
|
||||||
DestinationPath);
|
DestinationPath);
|
||||||
@@ -123,10 +116,10 @@ namespace PSMinIO.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure destination directory exists
|
// Ensure destination directory exists
|
||||||
if (destinationInfo.Directory != null && !destinationInfo.Directory.Exists)
|
if (DestinationPath.Directory != null && !DestinationPath.Directory.Exists)
|
||||||
{
|
{
|
||||||
destinationInfo.Directory.Create();
|
DestinationPath.Directory.Create();
|
||||||
WriteVerboseMessage("Created destination directory: {0}", destinationInfo.Directory.FullName);
|
WriteVerboseMessage("Created destination directory: {0}", DestinationPath.Directory.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse compression level
|
// Parse compression level
|
||||||
@@ -141,43 +134,41 @@ namespace PSMinIO.Cmdlets
|
|||||||
_ => "Create zip archive"
|
_ => "Create zip archive"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ShouldProcess(DestinationPath, operationDescription))
|
if (ShouldProcess(DestinationPath.FullName, operationDescription))
|
||||||
{
|
{
|
||||||
ExecuteOperation("CreateZipArchive", () =>
|
var result = ExecuteOperation("CreateZipArchive", () =>
|
||||||
{
|
{
|
||||||
WriteVerboseMessage("Creating zip archive: {0}", DestinationPath);
|
WriteVerboseMessage("Creating zip archive: {0}", DestinationPath.FullName);
|
||||||
WriteVerboseMessage("Compression level: {0}, Mode: {1}", CompressionLevel, Mode);
|
WriteVerboseMessage("Compression level: {0}, Mode: {1}", CompressionLevel, Mode);
|
||||||
|
|
||||||
ZipCreationResult result;
|
ZipCreationResult zipResult;
|
||||||
using (var zipBuilder = ZipBuilder.CreateFile(this, DestinationPath, archiveMode))
|
using (var zipBuilder = ZipBuilder.CreateFile(this, DestinationPath.FullName, archiveMode))
|
||||||
{
|
{
|
||||||
switch (ParameterSetName)
|
switch (ParameterSetName)
|
||||||
{
|
{
|
||||||
case "Files":
|
case "Files":
|
||||||
result = ProcessFiles(zipBuilder, compressionLevel);
|
zipResult = ProcessFiles(zipBuilder, compressionLevel);
|
||||||
break;
|
break;
|
||||||
case "Directory":
|
case "Directory":
|
||||||
result = ProcessDirectory(zipBuilder, compressionLevel);
|
zipResult = ProcessDirectory(zipBuilder, compressionLevel);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Unknown parameter set: {ParameterSetName}");
|
throw new InvalidOperationException($"Unknown parameter set: {ParameterSetName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteVerboseMessage("Zip archive created successfully: {0}", DestinationPath);
|
WriteVerboseMessage("Zip archive created successfully: {0}", DestinationPath.FullName);
|
||||||
WriteVerboseMessage("Archive summary: {0} files, {1} -> {2} ({3:F1}% compression)",
|
WriteVerboseMessage("Archive summary: {0} files, {1} -> {2} ({3:F1}% compression)",
|
||||||
result.FileCount,
|
zipResult.FileCount,
|
||||||
SizeFormatter.FormatBytes(result.TotalUncompressedSize),
|
SizeFormatter.FormatBytes(zipResult.TotalUncompressedSize),
|
||||||
SizeFormatter.FormatBytes(result.TotalCompressedSize),
|
SizeFormatter.FormatBytes(zipResult.TotalCompressedSize),
|
||||||
result.CompressionEfficiency);
|
zipResult.CompressionEfficiency);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
return zipResult;
|
||||||
{
|
}, $"Destination: {DestinationPath.FullName}, ParameterSet: {ParameterSetName}");
|
||||||
WriteObject(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
// Always return the result object
|
||||||
}, $"Destination: {DestinationPath}, ParameterSet: {ParameterSetName}");
|
WriteObject(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +180,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
if (Path == null || Path.Length == 0)
|
if (Path == null || Path.Length == 0)
|
||||||
{
|
{
|
||||||
WriteWarning("No files provided for zip archive");
|
WriteWarning("No files provided for zip archive");
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out files that don't exist
|
// Filter out files that don't exist
|
||||||
@@ -204,7 +195,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
if (validFiles.Length == 0)
|
if (validFiles.Length == 0)
|
||||||
{
|
{
|
||||||
WriteWarning("No valid files found for zip archive");
|
WriteWarning("No valid files found for zip archive");
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteVerboseMessage("Adding {0} files to zip archive", validFiles.Length);
|
WriteVerboseMessage("Adding {0} files to zip archive", validFiles.Length);
|
||||||
@@ -212,7 +203,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
// Add files to zip
|
// Add files to zip
|
||||||
zipBuilder.AddFiles(validFiles.Cast<FileSystemInfo>(), BasePath, compressionLevel);
|
zipBuilder.AddFiles(validFiles.Cast<FileSystemInfo>(), BasePath, compressionLevel);
|
||||||
|
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -228,7 +219,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
ErrorCategory.ObjectNotFound,
|
ErrorCategory.ObjectNotFound,
|
||||||
Directory);
|
Directory);
|
||||||
ThrowTerminatingError(errorRecord);
|
ThrowTerminatingError(errorRecord);
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get files from directory
|
// Get files from directory
|
||||||
@@ -236,7 +227,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
if (files.Length == 0)
|
if (files.Length == 0)
|
||||||
{
|
{
|
||||||
WriteWarning($"No files found in directory: {Directory.FullName}");
|
WriteWarning($"No files found in directory: {Directory.FullName}");
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteVerboseMessage("Adding directory to zip: {0} ({1} files)", Directory.Name, files.Length);
|
WriteVerboseMessage("Adding directory to zip: {0} ({1} files)", Directory.Name, files.Length);
|
||||||
@@ -247,7 +238,7 @@ namespace PSMinIO.Cmdlets
|
|||||||
// Add files to zip
|
// Add files to zip
|
||||||
zipBuilder.AddFiles(files.Cast<FileSystemInfo>(), basePath, compressionLevel);
|
zipBuilder.AddFiles(files.Cast<FileSystemInfo>(), basePath, compressionLevel);
|
||||||
|
|
||||||
return zipBuilder.CreateResult(DestinationPath);
|
return zipBuilder.CreateResult(DestinationPath.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user