I'm trying to update the contents of a zip file created from an Excel document. I want to replace some of the content of \zipfile\xl\connections.xml.
This partial script will list the contents of the zip file:
$shell_app = new-object -com shell.application
$zip = "$destination\exceltemplates\Templates\Template1.xlsx.zip"
$zip_file=$shell_app.NameSpace($zip)
$zip_file.Items() | Select Path
But every update method I've tried has generated an error. What's the next step needed to access and update a file in the zip file?
This is not very complicated. Using PowerShell v3.0 or higher (and the standard .NET System.IO libraries) is easier.
# Parameters
$zipfileName = "E:\temp\WebsitePackage.zip"
$fileToEdit = "robots.txt"
$contents = "User-agent: *
Disallow: /"
# Open zip and find the particular file (assumes only one inside the Zip file)
Add-Type -assembly System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName,"Update")
$robotsFile = $zip.Entries.Where({$_.name -eq $fileToEdit})
# Update the contents of the file
$desiredFile = [System.IO.StreamWriter]($robotsFile).Open()
$desiredFile.BaseStream.SetLength(0)
$desiredFile.Write($contents)
$desiredFile.Flush()
$desiredFile.Close()
# Write the changes and close the zip file
$zip.Dispose()
Write-Host "zip file updated"
The next problem is how to quickly check that your changes were successful? A simple adaptation of the script allows you to read the contents of file inside a Zip file:
# Parameters
$zipfileName = "E:\temp\WebsitePackage.zip"
$fileToRead = "robots.txt"
# Open zip and find the particular file (assumes only one inside the Zip file)
Add-Type -assembly System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName,"Update")
$robotsFile = $zip.Entries.Where({$_.name -eq $fileToRead})
# Read the contents of the file
$desiredFile = [System.IO.StreamReader]($robotsFile).Open()
$text = $desiredFile.ReadToEnd()
# Output the contents
$text
$desiredFile.Close()
$desiredFile.Dispose()
# Close the zip file
$zip.Dispose()
There is useful background material at this article: https://mcpmag.com/articles/2014/09/29/file-frontier-part-6.aspx
I had to replace the version xml file (NuGetApp1.nuspec) in a zipped nuget package, like so:
Original file content in zip:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>NuGetApp1</id>
<version>1.1.3-dev22222</version>
<title>NuGetApp1</title>
.....
Required :
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>NuGetApp1</id>
<version>1.0.0.0</version>
<title>NuGetApp1</title>
.....
Powershell Script:
$replaceWithVersion="1.0.0.0"
$path = "c:\temp\testnuget"
$files = Get-ChildItem *.nupkg -Path $path
foreach($fileNuget in $files)
{
$target = $fileNuget.FullName -replace "[0-9]+(\.([0-9]+|\*)){1,4}", $replaceWithVersion
Copy-Item $fileNuget.FullName -Destination $target
$zipfileName = $target
$fileToEdit = "*.nuspec"
# Open zip and find the particular file (assumes only one inside the Zip file)
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName,"Update")
$nuspecFile = $zip.Entries.Where({$_.name -like $fileToEdit})
# Read the contents of the file
$desiredFile = [System.IO.StreamReader]($nuspecFile).Open()
$text = $desiredFile.ReadToEnd()
$desiredFile.Close()
$desiredFile.Dispose()
$text = $text -replace '<version>[\s\S]*?<\/version>',"<version>$replaceWithVersion</version>"
#update file with new content
$desiredFile = [System.IO.StreamWriter]($nuspecFile).Open()
$desiredFile.BaseStream.SetLength(0)
# Insert the $text to the file and close
$desiredFile.Write($text)
$desiredFile.Flush()
$desiredFile.Close()
# Write the changes and close the zip file
$zip.Dispose()
Write-Host "zip file updated"
}
Happy hunting:-)!
When searching, this is a question I found, so I thought I would venture to answer. In this generic script, the invocation of VB is not used, instead the dotnet import of System.IO.Compression.FileSystem. Hope this helps someone. Who knows, might even be me in the future! LOL
# The zip file to be updated
$file = Get-ChildItem ~\file.zip
# Load ZipFile (Compression.FileSystem) if necessary
try { $null = [IO.Compression.ZipFile] }
catch { [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') }
# Open zip file with update mode (Update, Read, Create -- are the options)
try { $fileZip = [System.IO.Compression.ZipFile]::Open( $file, 'Update' ) }
catch { throw "Another process has locked the '$file' file." }
# Finding the specific file within the zip file
<#
NOTE: These entries have the directories separated with a forward slash (/) instead of MS convention
of a backward slash (\). Even though this is regex it seems the forward slash does not need to be escaped (\/).
NOTE2: Because this is regex '.*' must be used instead of a simple '*'
#>
$fileZip.Entries | Where-Object { $_.FullName -match '/subdir/.*/desiredfile.xml' }
# If needed, read the contents of specific file to $text and release the file so to use streamwriter later
$desiredFile = [System.IO.StreamReader]($fileZip.Entries | Where-Object { $_.FullName -match '/subdir/.*/desiredfile.xml' }).Open()
$text = $desiredFile.ReadToEnd()
$desiredFile.Close()
$desiredFile.Dispose()
# If needed, manipulate $text however for the update
$text = $text -replace '\n', [char]30
# Re-open the file this time with streamwriter
$desiredFile = [System.IO.StreamWriter]($fileZip.Entries | Where-Object { $_.FullName -match '/subdir/.*/desiredfile.xml' }).Open()
# If needed, zero out the file -- in case the new file is shorter than the old one
$desiredFile.BaseStream.SetLength(0)
# Insert the $text to the file and close
$desiredFile.Write($text -join "`r`n")
$desiredFile.Flush()
$desiredFile.Close()
# Write the changes and close the zip file
$fileZip.Dispose()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With