PowerShell: Compressing Files and Folders

# Set variables
$compressTarget="C:\Temp\Win2016_Std_Template.ova"
$parentFolder=split-path $compressTarget -Parent
$grandParentFolder=split-path $parentFolder -Parent
$compressedFile=$grandParentFolder+"\compressed.zip"

# PowerShell version 5
Compress-Archive -Path $compressTarget -DestinationPath $compressedFile

# PowerShell version 4 (Method 1: using .NET only works to compress folder)
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($parentFolder, $compressedFile)

# PowerShell version 4 (method 2)
$compressTarget = "E:\WSUS Content\UpdateServicesPackages\Win2016_Std_Template - Updated 10-23-2018.ova"
$parentFolder = split-path $compressTarget -Parent
$compressedFile=$parentFolder+"\compressed.zip"
Add-Type -As System.IO.Compression.FileSystem
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile([System.IO.Compression.ZipFile]::Open( $compressedFile, "Update" ), $compressTarget, (Resolve-Path $compressTarget -Relative).TrimStart(".\"), "Optimal")

# Snipped obtained from the Internet: https://ss64.com/ps/zip.txt
Add-Type -As System.IO.Compression.FileSystem

# Warning: this function cannot compress large files
function New-ZipFile {
	#.Synopsis
	#  Create a new zip file, optionally appending to an existing zip...
	[CmdletBinding()]
	param(
		# The path of the zip to create
		[Parameter(Position=0, Mandatory=$true)]
		$ZipFilePath,
 
		# Items that we want to add to the ZipFile
		[Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
		[Alias("PSPath","Item")]
		[string[]]$InputObject = $Pwd,
 
		# Append to an existing zip file, instead of overwriting it
		[Switch]$Append,
 
		# The compression level (defaults to Optimal):
		#   Optimal - The compression operation should be optimally compressed, even if the operation takes a longer time to complete.
		#   Fastest - The compression operation should complete as quickly as possible, even if the resulting file is not optimally compressed.
		#   NoCompression - No compression should be performed on the file.
		[System.IO.Compression.CompressionLevel]$Compression = "Optimal"
	)
	begin {
		# Make sure the folder already exists
		[string]$File = Split-Path $ZipFilePath -Leaf
		[string]$Folder = $(if($Folder = Split-Path $ZipFilePath) { Resolve-Path $Folder } else { $Pwd })
		$ZipFilePath = Join-Path $Folder $File
		# If they don't want to append, make sure the zip file doesn't already exist.
		if(!$Append) {
			if(Test-Path $ZipFilePath) { Remove-Item $ZipFilePath }
		}
		$Archive = [System.IO.Compression.ZipFile]::Open( $ZipFilePath, "Update" )
	}
	process {
		foreach($path in $InputObject) {
			foreach($item in Resolve-Path $path) {
				# Push-Location so we can use Resolve-Path -Relative
				Push-Location (Split-Path $item)
				# This will get the file, or all the files in the folder (recursively)
				foreach($file in Get-ChildItem $item -Recurse -File -Force | % FullName) {
					# Calculate the relative file path
					$relative = (Resolve-Path $file -Relative).TrimStart(".\")
					# Add the file to the zip
					$null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Archive, $file, $relative, $Compression)
				}
				Pop-Location
			}
		}
	}
	end {
		$Archive.Dispose()
		Get-Item $ZipFilePath
	}
}
New-Zipfile -ZipFilePath $compressedFile -Append $compressTarget

<# Sample error when compressing large files (25GB in this sample)
Exception calling "CreateEntryFromFile" with "4" argument(s): "Stream was too long."
At line:42 char:1
+ $null = [System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Archive, ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : IOException
#>

Leave a Reply

Your email address will not be published. Required fields are marked *