簡體   English   中英

當我的構造函數接收大量字節時,如何在 PowerShell 腳本中使用 New-Object cmdlet?

[英]How can I use the New-Object cmdlet in a PowerShell script when my constructor takes in a large array of bytes?

我正在編寫一個 PowerShell 函數/腳本(使用 Windows 10 附帶的版本,我相信它是 5.0)以接收 GZip 壓縮的 Base64 字符串並解壓縮它,然后在假設原始未壓縮/解碼字符串是Unicode 編碼。

我正在嘗試使用此構造函數和 New-Object cmdlet 實例化 MemoryStream 類型的新 object 實例。 它有一個參數,即一個字節數組。

PowerShell 無法找到接受我試圖作為構造函數參數傳遞的字節數組的有效重載。 我認為問題是由於數組的長度相對較大。 請在下面查看我的代碼:

Function DecompressString()
{
    param([parameter(Mandatory)][string]$TextToDecompress)
    
    try
    {
        $bytes = [Convert]::FromBase64String($TextToDecompress)

        $srcStreamParameters = @{
            TypeName = 'System.IO.MemoryStream'
            ArgumentList = ([byte[]]$bytes)
        }

        $srcStream = New-Object @srcStreamParameters
        $dstStream = New-Object -TypeName System.IO.MemoryStream
        
        $gzipParameters = @{
            TypeName = 'System.IO.Compression.GZipStream'
            ArgumentList = ([System.IO.Stream]$srcStream, [System.IO.Compression.CompressionMode]::Decompress)
        }
        
        $gzip = New-Object @gzipParameters
        $gzip.CopyTo($dstStream)
        $output = [Text.Encoding]::Unicode.GetString($dstStream.ToArray())
        Write-Output $output
    }
    catch
    {
        Write-Host "$_" -ForegroundColor Red
    }
    finally
    {
        if ($gzip -ne $null) { $gzip.Dispose() }
        if ($srcStream -ne $null) { $srcStream.Dispose() }
        if ($dstStream -ne $null) { $dstStream.Dispose() }
    }
}

DecompressString
$ExitPrompt = Read-Host -Prompt 'Press Enter to Exit'

我收到的錯誤消息是: Cannot find an overload for "MemoryStream" and the argument count: "1764".

誰能闡明我如何讓腳本解釋器正確使用帶有大字節數組的構造函數?

我想我會分享這個問題的答案,我發現這個問題非常有趣, Lance U. Matthews在他的有用評論中已經提供了錯誤的解決方案,方法是在分配給New-ObjectArgumentList$bytes之前添加一元運算, New-Object ,通過這樣做, $bytes作為單個參數(字節數組)而不是作為單個元素傳遞給System.IO.MemoryStream的構造函數:

$bytes = [Convert]::FromBase64String($compressedString)
$srcStreamParameters = @{
    TypeName     = 'System.IO.MemoryStream'
    ArgumentList = , $bytes
}

$srcStream = New-Object @srcStreamParameters

PowerShell 5.0開始,您可以使用以下語法構建您的 memory stream,這更加高效和直接:

$srcStream = [System.IO.MemoryStream]::new($bytes)

至於功能(壓縮擴展),我想分享我對這些很酷的功能的看法。

using namespace System.Text
using namespace System.IO
using namespace System.IO.Compression
using namespace System.Collections
using namespace System.Management.Automation
using namespace System.Collections.Generic
using namespace System.Management.Automation.Language

Add-Type -AssemblyName System.IO.Compression

class EncodingCompleter : IArgumentCompleter {
    [IEnumerable[CompletionResult]] CompleteArgument (
        [string] $commandName,
        [string] $parameterName,
        [string] $wordToComplete,
        [CommandAst] $commandAst,
        [IDictionary] $fakeBoundParameters
    ) {
        [CompletionResult[]] $arguments = foreach($enc in [Encoding]::GetEncodings().Name) {
            if($enc.StartsWith($wordToComplete)) {
                [CompletionResult]::new($enc)
            }
        }
        return $arguments
    }
}
  • 字符串壓縮到Base64 GZip 壓縮字符串
function Compress-GzipString {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $String,

        [Parameter()]
        [ArgumentCompleter([EncodingCompleter])]
        [string] $Encoding = 'utf-8',

        [Parameter()]
        [CompressionLevel] $CompressionLevel = 'Optimal'
    )

    try {
        $enc       = [Encoding]::GetEncoding($Encoding)
        $outStream = [MemoryStream]::new()
        $gzip      = [GZipStream]::new($outStream, [CompressionMode]::Compress, $CompressionLevel)
        $inStream  = [MemoryStream]::new($enc.GetBytes($string))
        $inStream.CopyTo($gzip)
    }
    catch {
        $PSCmdlet.WriteError($_)
    }
    finally {
        $gzip, $outStream, $inStream | ForEach-Object Dispose
    }

    try {
        [Convert]::ToBase64String($outStream.ToArray())
    }
    catch {
        $PSCmdlet.WriteError($_)
    }
}
  • Base64 GZip 壓縮字符串擴展為字符串
function Expand-GzipString {
    [cmdletbinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string] $String,

        [Parameter()]
        [ArgumentCompleter([EncodingCompleter])]
        [string] $Encoding = 'utf-8'
    )

    try {
        $enc       = [Encoding]::GetEncoding($Encoding)
        $bytes     = [Convert]::FromBase64String($String)
        $outStream = [MemoryStream]::new()
        $inStream  = [MemoryStream]::new($bytes)
        $gzip      = [GZipStream]::new($inStream, [CompressionMode]::Decompress)
        $gzip.CopyTo($outStream)
        $enc.GetString($outStream.ToArray())
    }
    catch {
        $PSCmdlet.WriteError($_)
    }
    finally {
        $gzip, $outStream, $inStream | ForEach-Object Dispose
    }
}

對於小長度比較,查詢Loripsum API

$loremIp = Invoke-RestMethod loripsum.net/api/10/long
$compressedLoremIp = Compress-GzipString $loremIp

$loremIp, $compressedLoremIp | Select-Object Length

Length
------
  8353
  4940

(Expand-GzipString $compressedLoremIp) -eq $loremIp # => Should be True

這兩個函數以及從文件路徑壓縮和從文件路徑擴展都可以在這個 repo上找到。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM