我有一个场景,需要获得一个嵌入在JSON响应中的安装程序,该响应是base64 64编码的。由于JSON字符串的大小相当大(180 MB),因此在使用标准PowerShell工具解码REST响应时会出现问题,因为它会导致在有限内存场景中抛出OutOfMemoryException (例如按WinRM内存配额)。
通过单个安装来提高环境中的内存配额是不可取的,而且我们也没有标准的工具来准备一个包,它的有效负载在简单的HTTP端点上不存在(我没有直接的权限来发布没有通过我们的构建系统执行的包)。在这种情况下,我的解决方案是以块的形式解码base64字符串。然而,虽然我有这个工作,但我仍然停留在这个过程的最后一点优化上。
目前,我正在使用MemoryStream从字符串中读取数据,但我需要提供一个byte[]
# $Base64String is a [ref] type
$memStream = [IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($Base64String.Value))这并不令人意外地导致复制整个base64 64编码字符串的byte[]表示,并且在其当前形式中,内存效率甚至低于内置工具。这里没有看到的代码一次从$memStream读取大块1024字节,解码base64字符串并使用BinaryWriter将字节写入磁盘。这一切都很好,如果速度慢的话,因为我经常强制垃圾收集。但是,我希望将这个字节计数扩展到初始的MemoryStream,并且一次只从字符串中读取n字节。我的理解是,base64字符串必须以可以被4除的字节块解码。
问题是,[string].Substring([int], [int])基于字符串长度,而不是每个字符的字节数。JSON响应可以假定为UTF-8编码,但即使在这种假设下,UTF-8字符的长度在1-4字节之间。如何(直接或间接)子字符串(直接或间接)在PowerShell中的特定字节数,以便我可以从这个子字符串而不是完整的$Base64String创建MemoryStream
我将注意到,我已经探索了[[Text.Encoding].GetBytes([string], [int], [int])重载]的用法(然而,我也面临同样的问题,因为该方法期望字符串长度为字符计数,而不是字节计数,以便从起始索引中获取byte[]。
发布于 2022-06-13 18:59:42
为了回答基本问题“如何从PowerShell中的字符串中将特定数目的字节子字符串”,我编写了以下函数:
function Get-SubstringByByteCount {
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidateScript({ $null -ne $_ -and $_.Value -is [string] })]
[ref]$InputString,
[int]$FromIndex = 0,
[Parameter(Mandatory)]
[int]$ByteCount,
[ValidateScript({ [Text.Encoding]::$_ })]
[string]$Encoding = 'UTF8'
)
[long]$byteCounter = 0
[System.Text.StringBuilder]$sb = New-Object System.Text.StringBuilder $ByteCount
try {
while ( $byteCounter -lt $ByteCount -and $i -lt $InputString.Value.Length ) {
[char]$char = $InputString.Value[$i++]
[void]$sb.Append($char)
$byteCounter += [Text.Encoding]::$Encoding.GetByteCount($char)
}
$sb.ToString()
} finally {
if( $sb ) {
$sb = $null
[System.GC]::Collect()
}
}
}调用的工作方式如下:
Get-SubstringByByteCount -InputString ( [ref]$someString ) -ByteCount 8关于这一执行的一些说明:
[ref]类型,因为最初的目标是避免在有限内存场景中复制完整字符串。这个函数可以使用[string]类型重新实现。StringBuilder中,直到指定的字节数被写入为止。[Text.Encoding]::GetByteCount重载之一来确定的。可以通过参数指定编码,但编码值应该与[Text.Encoding]中可用的静态编码属性之一相匹配。默认情况下,written.$sb = $null和[System.GC]::Collect()用于在内存受限的环境中强制清除StringBuilder,但如果这不是concern.-FromIndex在-InputString中开始子字符串操作的起始位置,则可以省略。默认为从-InputString.开始计算的0
https://stackoverflow.com/questions/72605084
复制相似问题