带PowerShell的HTTP POST多部分/表单数据二进制数据
在W2K12 R2 x64上使用PS 5.1.14409.1005 就我所知,有两种方法可以做到这一点-带PowerShell的HTTP POST多部分/表单数据二进制数据,powershell,Powershell,在W2K12 R2 x64上使用PS 5.1.14409.1005 就我所知,有两种方法可以做到这一点- 按照RFC格式手动创建多部分/表单正文 使用MultipartFormDataContent.NET类型 我尝试了MultipartFormDataContent,但当我将构造的对象添加到Invoke WebRequest的body参数时,它不起作用(例如,请参见下面的示例),并且我成功地使用文本表单部分手动构造,但在使用二进制时遇到了问题。屏幕截图显示了一个来自powershell的Inv
Invoke WebRequest
的body参数时,它不起作用(例如,请参见下面的示例),并且我成功地使用文本表单部分手动构造,但在使用二进制时遇到了问题。屏幕截图显示了一个来自powershell的Invoke WebRequest
的fiddler请求捕获,它不起作用,另一个来自chrome,它起作用。二进制内容是不同的,我不知道如何获得与chrome相同的内容
以下是前几个十六进制字节,将实际文件与chrome和powershell在fiddler中发送的文件进行比较。其余的部分都有一些小的区别
file: 30 82 1D 32 02 01 03 30 82 1C F8
chrome: 30 82 1D 32 02 01 03 30 82 1C F8
ps: 30 2C 1D 32 02 01 03 30 2C 1C F8
这是我目前的代码。二进制部分由Get Content-Path$FilePath-Raw
填充
function Publish-RestApiFile {
param ([string] $Url, [hashtable] $Params, [string] $FilePath, [string] $FileParamName)
Add-Type -AssemblyName System.Web
$bnd = [System.Guid]::NewGuid().ToString().ToLower().Replace('-','').PadLeft(40,'-')
$content_type = "multipart/form-data; boundary=$bnd"
$LF = "`r`n"
$lines = @("--${bnd}${LF}")
$Params.GetEnumerator() | ForEach-Object {
$lines += "$('Content-Disposition: form-data; name="{0}"' -f $_.Name)${LF}${LF}$($_.Value)${LF}"
$lines += "--${bnd}${LF}"
}
$file_name = Split-Path -Path $FilePath -Leaf
$mime_type = [System.Web.MimeMapping]::GetMimeMapping($FilePath)
$ct = Get-Content -Path $FilePath -Raw
$lines += ('Content-Disposition: form-data; name="{0}"; filename="{1}"' -f $FileParamName,$file_name)+${LF}
$lines += ("Content-Type: ${mime_type}${LF}${LF}"),$ct,${LF}
$lines += ("--${bnd}--"+${LF})
$lines = $lines -join ''
Invoke-RestMethod -Uri $url -Method Post -Body $lines -ContentType $content_type -MaximumRedirection 0
}
显示当前代码生成的正文字符串-
这显示了PSInvoke WebRequest
(左)和chrome(右)在fiddler中捕获的请求的二进制差异。
下面是使用MultipartFormDataContent的示例。Fiddler显示对象正被强制转换为正文中的字符串,例如System.Net.Http.StringContent System.Net.Http.StringContent System.Net.Http.StreamContent系统.Net.Http.StreamContent
function Publish-RestApiFile2 {
param ([string] $Url, [hashtable] $Params, [string] $FilePath, [string] $FileParamName)
Add-Type -AssemblyName System.Net.Http, System.Web
$mime_type = [System.Web.MimeMapping]::GetMimeMapping($FilePath)
$form = [System.Net.Http.MultipartFormDataContent]::New()
foreach ($param in $Params.GetEnumerator()) {
$header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
$header.Name = $param.Name
$content = [System.Net.Http.StringContent]::new($param.Value)
$content.Headers.ContentDisposition = $header
$form.Add($content)
}
$stream = [System.IO.FileStream]::New($FilePath, 'Open')
$header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
$header.Name = $FileParamName
$header.FileName = (Split-Path -Path $FilePath -Leaf)
$content = [System.Net.Http.StreamContent]::New($stream)
$content.Headers.ContentDisposition = $header
$content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse($mime_type)
$form.Add($content)
Invoke-WebRequest -Uri $Url -Method Post -Body $form -ContentType 'multipart/form-data'
#Invoke-WebRequest -Uri $Url -Method Post -Body $form
$stream.Close(); $stream.Dispose()
}
我使用所有.NET类型而不是
Invoke-WebRequest
-
如果它有助于某人-
Add-Type -AssemblyName System.Net.Http
$url = "https://server.local/restendpoint"
$file = "C:\cert.pfx"
$fs = [System.IO.FileStream]::New($file, [System.IO.FileMode]::Open)
$s1 = New-Object System.Net.Http.StringContent 'alias_value'
$s2 = New-Object System.Net.Http.StringContent 'password_value'
$f1 = New-Object System.Net.Http.StreamContent $fs
$handler = New-Object System.Net.Http.HttpClientHandler
$handler.AllowAutoRedirect = $false # Don't follow after post redirect code 303
$client = New-Object System.Net.Http.HttpClient -ArgumentList $handler
$client.DefaultRequestHeaders.ConnectionClose = $true # Disable keep alive, get a 200 response rather than 303
$form = New-Object System.Net.Http.MultipartFormDataContent
$form.Add($s1, 'alias')
$form.Add($s2, 'password')
$form.Add($f1, 'file', 'cert.pfx')
$rsp = $client.PostAsync($u, $form).Result
$rsp.IsSuccessStatusCode # false if 303
$rsp.StatusCode -eq 303 # true if 303
$fs.Close(); $fs.Dispose()
您是否尝试过用[Io.File]::ReadAllBytes()替换Get内容?我发现Get内容存在许多与性能相关的问题,我会不惜一切代价避免这些问题。