4

I can convert files to B64 then back again, i would like to compress the data before converting to B64. Gzip doesnt have to be used, any type of compression or size reduction is fine, I think compression is the answer if I can get it somewhere in the conversion process because B64 uses more space than the original file when stored.

Current situation: File -> B64 + B64 -> File (Working fine with no temp files)

Goal: File->Compression->B64 + B64->Decompression->File (With no temp files)

Working non compressed example

FileToB64Text.ps1

[Convert]::ToBase64String([IO.File]::ReadAllBytes('test.pdf')) | Out-File 'testpdf.txt"

B64TextToFile.ps1

$file = Get-Content 'testpdf.txt'
[IO.File]::WriteAllBytes('test2.pdf', [Convert]::FromBase64String('$file'))

I stumbled across another way to do this with compression, which seems great since B64 conversion increases filesize, but the example is using TextToB64/B64ToText instead of FilesToB64/B64ToFiles. A way to use files as an initial input and final output AND having the compression is what I am trying to achieve.

CompressAndEncode.ps1

$s = Get-Content 'DecodedText.txt'
$ms = New-Object System.IO.MemoryStream
$cs = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Compress)
$sw = New-Object System.IO.StreamWriter($cs)
$sw.Write($s)
$sw.Close();
$s = [System.Convert]::ToBase64String($ms.ToArray()) | Out-File 'b64output.txt'

DecodeAndDecompress.ps1

$encodedstring = Get-Content 'b64output.txt'
$data = [System.Convert]::FromBase64String("$encodedstring")
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0,0) | Out-Null
$sr = New-Object System.IO.StreamReader(New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Decompress))
$sr.ReadToEnd() | Out-File 'DecodedText.txt
  • 2
    The main idea is that if your gzipping to a file you shouldn't convert the bytes to b64, there is no need for that and would only increase the file size for no reason. b64 is used mainly for transport of information. – Santiago Squarzon Jul 15 '23 at 19:15

1 Answers1

2

The main idea is that if you're gzipping to a file you shouldn't convert the compressed bytes to b64, there is no need for that and would only increase the file size for no reason as b64 is used mainly for transport of information.

  • If you want to Gzip compress to a file:
$infile = Get-Item path\to\myfiletocompress.ext
$instream = $infile.OpenRead()
$outstream = (New-Item path\to\mygzfile.gz).OpenWrite()
$gzip = [System.IO.Compression.GZipStream]::new(
    $outstream,
    [System.IO.Compression.CompressionLevel]::Optimal)
$instream.CopyTo($gzip)

$gzip.Dispose()
$outstream.Dispose()
$instream.Dispose()
  • If you want to expand a Gzip file:
$infile = Get-Item path\to\mygzfile.gz
$instream = $infile.OpenRead()
$outstream = (New-Item path\to\myexpandedgzfile.ext -Force).OpenWrite()
$gzip = [System.IO.Compression.GZipStream]::new(
    $instream,
    [System.IO.Compression.CompressionMode]::Decompress)
$gzip.CopyTo($outstream)

$gzip.Dispose()
$outstream.Dispose()
$instream.Dispose()
  • If you want to read the Gzip file content:
$infile = Get-Item mygzfile.gz
$instream = $infile.OpenRead()
$gzip = [System.IO.Compression.GZipStream]::new(
    $instream,
    [System.IO.Compression.CompressionMode]::Decompress)
$reader = [System.IO.StreamReader] $gzip
$reader.ReadToEnd()

$gzip.Dispose()
$reader.Dispose()
$instream.Dispose()
  • If you have a gzip b64 string in a file and you want to read its content:
$instream = [System.IO.MemoryStream]::new(
    [System.Convert]::FromBase64String((Get-Content .\mygzfile.gz -Raw)))
$gzip = [System.IO.Compression.GZipStream]::new(
    $instream,
    [System.IO.Compression.CompressionMode]::Decompress)
$outstream = [System.IO.MemoryStream]::new()
$gzip.CopyTo($outstream)

$gzip.Dispose()
$instream.Dispose()

$outstream.Seek(0, [System.IO.SeekOrigin]::Begin)
$reader = [System.IO.StreamReader] $outstream
$reader.ReadToEnd()

$reader.Dispose()
$outstream.Dispose()

If you would like to simplify this process, you can give PSCompression a try:

# compression
Compress-GZipArchive path\to\filetocompress path\to\destination.gz

# expansion to the success stream
Expand-GzipArchive path\to\compressed.gz

# expansion to a file
Expand-GzipArchive path\to\compressed.gz path\to\expanded

# expansion of gzip b64 string
Get-Content path\to\compressed.gz | ConvertFrom-GzipString
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • Sorry I am storing the data in text format so the end result i need help with needs to be B64 (hopefully compressed as example 1 already accomplishes this), I need to be able to open and scan through the file with a regex later. – PlayLORD-SysOp Jul 15 '23 at 19:41
  • @PlayLORD-SysOp why not store it as binary and then read it as shown in the 3rd example? – Santiago Squarzon Jul 15 '23 at 19:44
  • I am trying to add compression to this - https://github.com/illsk1lls/BAG - So I cannot deviate too much as I will have to rewrite the whole concept. I'm boxed in with the method. I haven't learned how to deal with binary streams yet. If compression isnt possible it's fine, but it is worth asking for sure. The above question contains more readable/simple versions of what I'm using in the script. – PlayLORD-SysOp Jul 15 '23 at 19:48
  • 1
    @PlayLORD-SysOp see last edit. i dont see the point in storing a gzip b64 string in a file but its your code not mine – Santiago Squarzon Jul 15 '23 at 19:56
  • The reason is that right now its storing an uncompressed B64 string which is using even more unnecessary space than a compressed string would.. Right now 700mb converts over to ~950.. – PlayLORD-SysOp Jul 15 '23 at 19:59
  • 1
    @PlayLORD-SysOp well the answer to "how to read a gzip b64 string from a file" is there in my answer. you can give a try – Santiago Squarzon Jul 15 '23 at 20:02
  • I can use your method if I can figure out how to take a normal file and zip it then convert it to base64 without making a temp file. I think even output needs a temp in this example, but because I dont understand this yet fully I still need help with the input. From your example I would need to change $outstream = (New-Item path\to\mygzfile.gz).OpenWrite() to something i could contain inside something like this [Convert]::ToBase64String([IO.File]::ReadAllBytes('$somestreamimsuredoesntuseIO.FILE')) I know thats not the cmd but the file will still be whole no? So thats the only way i know to – PlayLORD-SysOp Jul 15 '23 at 20:59
  • ```[System.IO.MemoryStream]::new( [System.Convert]::ToBase64String((Command that Creates GZIP without a temp?)))``` i meant something like this which I also know is wrong but makes me look slightly less stupid. =P – PlayLORD-SysOp Jul 15 '23 at 21:06
  • @PlayLORD-SysOp you need to use a memory stream and write the bytes to the gzip stream. i'm just not sure how many questions you have enclosed into a single question. i thought your issue for this SO question was "how to read a gzip b64 string from a file" right? – Santiago Squarzon Jul 15 '23 at 21:07
  • No the question, and I will edit it now is. Currently I am doing File->B64 and back to b64text>file without any temp files I want to do File>Compression>b64text and back b64text>decompression>file without any temp files. - The second example i posted was mainly to show that ive seen its possible, however I dont fully understand that example. – PlayLORD-SysOp Jul 15 '23 at 21:18