I am having trouble trying to call a function for a script that I'm using to build a list of zip files in a folder on my PC. The final CSV I need is to create is a list of the zip files with their uncompressed sizes. Here is what I have so far (compiled from several posts):
Function to get the uncompressed size:
function Get-UncompressedZipFileSize {
param (
$Path
)
$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace($Path)
$size = 0
foreach ($item in $zip.items()) {
if ($item.IsFolder) {
$size = Get-UncompressedZipFileSize -Path $item.Path
} else {
$size = $item.size
}
}
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$shell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
return $size
}
Here is my Array Creation:
$arr = @()
gci C:\zips -recurse | ? {$_.PSIsContainer -eq $False} | % {
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Name $_.Name
$obj | Add-Member NoteProperty FullPath $_.FullName
$arr = $obj
}
$arr | Export-CSV -notypeinformation c:\zips
I'm stuck at creating a new member object into my array that will call the get-uncompressedzipfilesize function to pass that size back into the array as a new column in my zip. Is something like this even possible.?
CodePudding user response:
To make this simpler, since you're only calling your function to get the size of the current folder (zip), you can use a Calculated Property for this:
$Path = "C:\Zips"
Get-ChildItem -Path $Path -Directory -Recurse |
Select-Object -Property Name, FullName,
@{
Name = "Size"
Expression = {
Get-UncompressedZipFileSize -Path $_.FullName
}
} | Export-Csv -Path "$Path\zip.csv" -Force -NoTypeInformation -Append
On another note, if you ever find yourself explicitly adding to an array, take advantage of PowerShell's pipeline streaming.
$Path = "C:\Zips"
Get-ChildItem -Path $Path -Directory -Recurse |
ForEach-Object -Process {
[PSCustomObject]@{
Name = $_.Name
FullPath = $_.FullName
Size = Get-UncompressedZipFileSize -Path $_.FullName
} | Export-Csv -Path "$Path\zip.csv" -Force -NoTypeInformation -Append
}
Not only is adding to a fixed array ( =
) computationally expensive (if you have a large directory), it is slow. Fixed arrays mean just that, they are a fixed size and in order for you to add to it, it needs to be broken down and recreated. An alternate solution would by an arraylist but, in this case - and in most cases - it's not needed.
Get-ChildItem
also includes a-Directory
switch to search for just folders. Presented in V3.- I would recommend searching for the file extension of the compressed folders as well so you don't run into any issues using
-Filter
.
CodePudding user response:
Here is an alternative using ZipFile Class
. The SizeConvert
function is inspired from this answer. The output of the Get-ZipFileSize
would be the absolute path of the Zip File, it's compressed and expanded size and it's formatted sizes (i.e.: 7.88 MB
instead of 8262942
).
using namespace System.IO
using namespace System.IO.Compression
using namespace System.Linq
function SizeConvert {
[cmdletbinding()]
param(
[object]$Length,
[int]$DecimalPoints = 2
)
$suffix = "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
$index = 0
while ($Length -ge 1kb) {
$Length /= 1kb
$index
}
return [string]::Format(
'{0} {1}',
[math]::Round($Length, $DecimalPoints), $suffix[$index]
)
}
function Get-ZipFileSize {
[cmdletbinding()]
param(
[parameter(ValueFromPipeline)]
[FileInfo]$Path
)
process {
try
{
$zip = [ZipFile]::OpenRead($Path.FullName)
$compressedSize = [Enumerable]::Sum([int64[]]$zip.Entries.CompressedLength)
$expandedSize = [Enumerable]::Sum([Int64[]]$zip.Entries.Length)
[pscustomobject]@{
FilePath = $Path.FullName
RawExpanded = $expandedSize
RawCompressed = $compressedSize
FormattedExpanded = SizeConvert $expandedSize -DecimalPoints 3
FormattedCompressed = SizeConvert $compressedSize -DecimalPoints 3
}
}
catch
{
$PSCmdlet.WriteError($_)
}
finally
{
if($zip -is [System.IDisposable]) {
$zip.Dispose()
}
}
}
}
Get-ChildItem -Filter *.zip -Recurse | Get-ZipFileSize | Export-Csv ....