Kaelan Fouwels
Kaelan Fouwels

Reputation: 1185

Write bytes to a file natively in PowerShell

I have a script for testing an API which returns a base64 encoded image. My current solution is this.

$e = (curl.exe -H "Content-Type: multipart/form-data" -F "[email protected]" localhost:5000)
$decoded = [System.Convert]::FromBase64CharArray($e, 0, $e.Length)
[io.file]::WriteAllBytes('out.png', $decoded) # <--- line in question

Get-Content('out.png') | Format-Hex

This works, but I would like to be able to write the byte array natively in PowerShell, without having to source from [io.file].

My attempts of writing $decoded in PowerShell all resulted in writing a string-encoded list of the integer byte values. (e.g.)

42
125
230
12
34
...

How do you do this properly?

Upvotes: 35

Views: 70452

Answers (5)

9 Guy
9 Guy

Reputation: 319

What I used recently while extracting an exe from a zip file

    $newFile = New-Item -Path "C:\Temp" -Name "xyz.exe"  # -Force if overriding

    try {
        # open a writable FileStream
        $fileStream = $newFile.OpenWrite()

        # create stream writer
        $streamWriter = [System.IO.BinaryWriter]::new($fileStream)

        # write to stream
        $streamWriter.Write($SomeExecutableData)
    }
    finally {
        # clean up
        $streamWriter.Dispose()
        $fileStream.Dispose()
        # if the file is nastily big, [GC]::Collect()
    }

There might be a more modern method than [System.IO.BinaryWriter]::new($fileStream), I'm too lazy to look it up at the moment but might edit later.

Most of the credit goes to: https://stackoverflow.com/a/70595622/3875151

Upvotes: 0

Carson
Carson

Reputation: 8028

for Powershell 6 or above

# $byteData = Get-Content -Path $filePath -AsByteStream -Raw # slow
$byteData = Get-Content -Path $filePath -AsByteStream -ReadCount 0 -Raw | foreach { $_ }
# $byteData | Set-Content -Path $outputFile -AsByteStream -NoNewline # slow
Set-Content -Path $outputFile -Value $byteData -AsByteStream -NoNewline

for powershell 5

$byteData = Get-Content -Path $filePath -Encoding Byte -ReadCount 0 -Raw | foreach { $_ }
Set-Content -Path $outputFile -Value $byteData -Encoding Byte -NoNewline

Upvotes: 2

Mark Henderson
Mark Henderson

Reputation: 2696

Powershell Core (v6 and above) no longer have -Encoding byte as an option, so you'll need to use -AsByteStream, e.g:

Set-Content -Path C:\temp\test.jpg -AsByteStream

Upvotes: 21

Sophie Swett
Sophie Swett

Reputation: 3390

The Set-Content cmdlet lets you write raw bytes to a file by using the Byte encoding:

$decoded = [System.Convert]::FromBase64CharArray($e, 0, $e.Length)
Set-Content out.png -Value $decoded -Encoding Byte

(However, BACON points out in a comment that Set-Content is slow: in a test, it took 10 seconds, and 360 MB of RAM, to write 10 MB of data. So, I recommend not using Set-Content if that's too slow for your application.)

Upvotes: 54

Vesper
Vesper

Reputation: 18747

Running C# assemblies is native to PowerShell, therefore you are already writing bytes to a file "natively".

If you insist, you can use a construction like set-content test.jpg -value (([char[]]$decoded) -join ""), this has a drawback of adding #13#10 to the end of written data. With JPEGs it's bearable, but other files may get corrupt from this alteration. So please stick with byte-optimized routines of .NET instead of searching for "native" approaches - these are already native.

Upvotes: 17

Related Questions