繁体   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