Eric
Eric

Reputation: 1297

Modify and ZIP file in one statement

PSVersion 5.1.18362.2212

I would like to know if it is possible to read in a set of text files, modify their content and their filenames and store the results directly into a ZIP file?

The following reads text files in and modifies them, storing the change into a new file:

$xSource = "sourcefile.json"
$xTarget = "targetfile.json"
$replacement = "abc"
(Get-Content $xSource) | Foreach-Object {
  $_.replace('[XX]', $replacement).`
} | Set-Content -path $xTarget

Is it possible to modify this to store the target file directly into a ZIP file?

I was hoping something like the following would work, but I am unsure of how I can pass the new filename through to the ZIP? or whether the following works at all?

$xSource = "sourcefile.json"
$xTarget = "targetfile.json"
$xTargetZip = "target.zip"
$replacement = "abc"
(Get-Content $xSource) | Foreach-Object {
  $_.replace('[XX]', $replacement).`
} | Compress-Archive -Update -DestinationPath $xTargetZip

I get the impression that I would need to store the target files into a temporary folder and then pack them from there ... is there any way of avoiding a temporary folder?

Thanks in advance for any and all help.

Upvotes: 1

Views: 189

Answers (1)

Santiago Squarzon
Santiago Squarzon

Reputation: 60683

The solution to this is cumbersome but you asked for it, this is how you can write entries to a zip file without prior writing the updates of your Jsons to new files, in other words, having the contents of the files in memory and writing them to a zip entry.

References for the .NET Docs used here:

using namespace System.IO
using namespace System.IO.Compression

Add-Type -AssemblyName System.IO.Compression

try {    
    # be aware, DO NOT use relative paths here!
    $DestinationPath = 'path\to\test.zip'
    $destfs = [File]::Open($DestinationPath, [FileMode]::CreateNew)
    $zip    = [ZipArchive]::new($destfs, [ZipArchiveMode]::Update)

    Get-ChildItem -Path path\to\jsonfolder -Filter *.json | ForEach-Object {
        # `OpenText` uses UTF8 encoding, normally there shouldn't be any issues here
        # but you can also use `Get-Content` instead to get the file content
        $reader    = $_.OpenText()
        $content   = $reader.ReadToEnd() -replace 'hello', 'world'
        # this is yours to define, this is how each entry should be named
        $entryName = $_.BaseName + '-ToBeDetermined' + $_.Extension
        $zipEntry  = $zip.CreateEntry($entryName)
        $zipStream = $zipEntry.Open()
        $writer    = [StreamWriter]::new($zipStream)
        $writer.Write($content)
        $writer, $reader, $zipStream | ForEach-Object 'Dispose'
    }
}
finally {
    $zip, $destfs | ForEach-Object 'Dispose'
}

If you're looking to simplify the process demonstrated above, reading a zip archive and replacing the content of zip archive entries, you might find it easier with the PSCompression Module (Disclaimer: I'm the author of this module).

This is how the code would look using the module:

$zip = New-Item 'path\to\test.zip' -ItemType File
Get-ChildItem -Path path\to\jsonfolder -Filter *.json | ForEach-Object {
    $entryName = $_.BaseName + '-ToBeDetermined' + $_.Extension
    ($_ | Get-Content -Raw) -replace 'hello', 'world' |
        New-ZipEntry -Destination $zip.FullName -EntryPath $entryName
}

Upvotes: 3

Related Questions