5

I've found a couple of resources (including Convert base64 string to file, which is practically a duplicate here since it's one of the resources I used to build this) but I can't seem to get it working.

I've got the following code (roughly - obviously it's stripped down,) and I can verify most of the steps of the process as per the comments.

$pic = Get-Content 'testpic.png'
# $pic looks like a binary dump.

$picBytes = [System.Text.Encoding]::Unicode.GetBytes($pic)
$ $picBytes is an array of bytes. Quite spammy.

$picEncoded = [Convert]::ToBase64String($picBytes)
# $picEncoded is indeed a Base64 string. Halfway there!

$picDecoded = [Convert]::FromBase64String($picEncoded)
# Also an array of bytes. I'm assuming they're right for now...

$outFile = "pic.png"
[IO.File]::WriteAllBytes($outFile,$picDecoded)
# but I get no output file and no error?

What am I missing here? For what it's worth, I'm willing to look at other solutions - but the Base64 is somewhat important (since I'm storing the data in the script.)

Epsilon
  • 73
  • 2
  • 5
  • What is the value of `$outFile`? – G42 Apr 03 '21 at 19:50
  • $outFile = "pic.png" Sorry, didn't separate that out from the other lines. – Epsilon Apr 03 '21 at 19:57
  • 2
    Check for pic.png in c:\windows\system32. And use a full path! – G42 Apr 03 '21 at 20:01
  • Nothing in system32, but... why. Just why. Yes, providing a full path appears to work (although my conversion is messed up, but that's a different rabbit hole.) – Epsilon Apr 03 '21 at 20:06
  • Forgot to say thanks @G42 - sorry, it's incredibly frustrating that I banged my head against this for 3 hours straight, only to find that the solution was a pattern that's unlike anything else in my script. – Epsilon Apr 03 '21 at 20:52
  • You might find it directly under C:\... cant remember where else .net methods default to. Either way glad to hear it's sorted. – G42 Apr 03 '21 at 20:52

1 Answers1

3

To read a binary file as-is into memory in PowerShell, use Get-Content's -AsByteStream switch (PowerShell (Core) 7+) / -Encoding Byte (Windows PowerShell, versions up to v5.1), and add the -Raw switch for efficiency when you're reading all bytes into memory at once:

# Windows PowerShell (up to v5.1).
# Note: In PowerShell (Core) v7+, you must use -AsByteStream instead of
#       -Encoding Byte
$picBytes = Get-Content testpic.png -Encoding Byte -Raw

Note: This change in syntax between the PowerShell editions is unfortunate, as discussed in GitHub issue #7986. If enough people show interest, it is conceivable that -Encoding Byte will be reintroduced for cross-edition consistency and compatibility.

$picBytes, as a [byte[]] array, can then be passed directly to [Convert]::ToBase64String()


To pass a file name/path to a .NET method, always pass a full path, never a relative path or mere file name:

This is necessary, because .NET's working directory usually differs from PowerShell's.
This discrepancy is unfortunate, but cannot be avoided, as explained in this answer.

In the simplest case - if your current location is a file-system location that is not based on a PowerShell-specific drive:

$outFile = "$PWD/pic.png" # Use *full path*
[IO.File]::WriteAllBytes($outFile, $picDecoded)

The fully robust approach requires more work:

$outFile = Join-Path (Get-Location -PSProvider FileSystem).ProviderPath pic.png
[IO.File]::WriteAllBytes($outFile, $picDecoded)
mklement0
  • 382,024
  • 64
  • 607
  • 775