简体   繁体   English

如何使用 PowerShell Invoke-RestMethod 发送多部分/表单数据

[英]How to send multipart/form-data with PowerShell Invoke-RestMethod

I'm trying to send a file via Invoke-RestMethod in a similar context as curl with the -F switch.我正在尝试通过 Invoke-RestMethod 在与 curl 类似的上下文中使用 -F 开关发送文件。

Curl Example卷曲示例

curl -F FileName=@"/path-to-file.name" "https://uri-to-post"

In powershell, I've tried something like this:在powershell中,我尝试过这样的事情:

$uri = "https://uri-to-post"
$contentType = "multipart/form-data"
$body = @{
    "FileName" = Get-Content($filePath) -Raw
}

Invoke-WebRequest -Uri $uri -Method Post -ContentType $contentType -Body $body
}

If I check fiddler I see that the body contains the raw binary data, but I get a 200 response back showing no payload has been sent.如果我检查提琴手,我会看到正文包含原始二进制数据,但我收到 200 响应,显示没有发送有效负载。

I've also tried to use the -InFile parameter with no luck.我也尝试过使用 -InFile 参数但没有成功。

I've seen a number of examples using a .net class, but was trying to keep this simple with the newer Powershell 3 commands.我已经看到了许多使用 .net 类的示例,但我试图用较新的 Powershell 3 命令来保持这个简单。

Does anyone have any guidance or experience making this work?有没有人有任何指导或经验使这项工作?

The accepted answer won't do a multipart/form-data request, but rather a application/x-www-form-urlencoded request forcing the Content-Type header to a value that the body does not contain.接受的答案不会执行multipart/form-data请求,而是执行application/x-www-form-urlencoded请求,将Content-Type标头强制为Content-Type不包含的值。

One way to send a multipart/form-data formatted request with PowerShell is:使用 PowerShell 发送multipart/form-data格式的请求的一种方法是:

$ErrorActionPreference = 'Stop'

$fieldName = 'file'
$filePath = 'C:\Temp\test.pdf'
$url = 'http://posttestserver.com/post.php'

Try {
    Add-Type -AssemblyName 'System.Net.Http'

    $client = New-Object System.Net.Http.HttpClient
    $content = New-Object System.Net.Http.MultipartFormDataContent
    $fileStream = [System.IO.File]::OpenRead($filePath)
    $fileName = [System.IO.Path]::GetFileName($filePath)
    $fileContent = New-Object System.Net.Http.StreamContent($fileStream)
    $content.Add($fileContent, $fieldName, $fileName)

    $result = $client.PostAsync($url, $content).Result
    $result.EnsureSuccessStatusCode()
}
Catch {
    Write-Error $_
    exit 1
}
Finally {
    if ($client -ne $null) { $client.Dispose() }
    if ($content -ne $null) { $content.Dispose() }
    if ($fileStream -ne $null) { $fileStream.Dispose() }
    if ($fileContent -ne $null) { $fileContent.Dispose() }
}

The problem here was what the API required some additional parameters.这里的问题是 API 需要一些额外的参数。 Initial request required some parameters to accept raw content and specify filename/size.初始请求需要一些参数来接受原始内容并指定文件名/大小。 After setting that and getting back proper link to submit, I was able to use:设置并取回正确的链接提交后,我可以使用:

Invoke-RestMethod -Uri $uri -Method Post -InFile $filePath -ContentType "multipart/form-data"

I found this post and changed it a bit我找到了这篇文章并对其进行了一些更改

$fileName = "..."
$uri = "..."

$currentPath = Convert-Path .
$filePath="$currentPath\$fileName"

$fileBin = [System.IO.File]::ReadAlltext($filePath)
$boundary = [System.Guid]::NewGuid().ToString()
$LF = "`r`n"
$bodyLines = (
    "--$boundary",
    "Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"",
    "Content-Type: application/octet-stream$LF",
    $fileBin,
    "--$boundary--$LF"
) -join $LF

Invoke-RestMethod -Uri $uri -Method Post -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines

For anyone wondering (like Jelphy) whether David's answer can be used with cookies/credentials, the answer is yes.对于任何想知道(如 Jelphy)大卫的答案是否可以与 cookie/凭据一起使用的人,答案是肯定的。

First set the session with Invoke-WebRequest:首先使用 Invoke-WebRequest 设置会话:

Invoke-WebRequest -Uri "$LoginUri" -Method Get -SessionVariable 'Session'

Then POST to the Login URL, which stores the authentication cookie in $Session :然后 POST 到登录 URL,它将身份验证 cookie 存储在$Session

$Response = Invoke-WebRequest -Uri "$Uri" -Method Post -Body $Body -WebSession $Session

The steps above are the standard way to deal with session in Powershell . 上述步骤是在 Powershell 中处理 session 的标准方法 But here is the important part.但这里是重要的部分。 Before creating the HttpClient, create an HttpClientHandler and set it's CookieContainer property with the cookies from the session:在创建 HttpClient 之前,创建一个 HttpClientHandler 并使用会话中的 cookie 设置它的 CookieContainer 属性:

$ClientMessageHandler = New-Object System.Net.Http.HttpClientHandler
$ClientMessageHandler.CookieContainer = $Session.Cookies

Then pass this object to the HttpClient constructor然后将此对象传递给 HttpClient 构造函数

$Client = [System.Net.Http.HttpClient]::new($ClientMessageHandler)

Voila, you now have an HttpClient with session cookies set automatically via Invoke-WebRequest.瞧,您现在拥有了一个 HttpClient,并通过 Invoke-WebRequest 自动设置了会话 cookie。 The rest of David's example should work (copied here for completeness): David 示例的其余部分应该可以工作(为了完整起见,复制到此处):

$MultipartFormData = New-Object System.Net.Http.MultipartFormDataContent
$FileStream = [System.IO.File]::OpenRead($FilePath)
$FileName = [System.IO.Path]::GetFileName($FilePath)
$FileContent = New-Object System.Net.Http.StreamContent($FileStream)
$MultipartFormData.Add($FileContent, $FieldName, $FileName)

$Result = $Client.PostAsync($url, $content).Result
$Result.EnsureSuccessStatusCode()

I had many files to upload with each request, so I factored out this last bit into a lambda function:每个请求我都有很多文件要上传,所以我将最后一点分解为一个 lambda 函数:

function Add-FormFile {
    param ([string]$Path, [string]$Name)

    if ($Path -ne "")
    {    
        $FileStream = [System.IO.File]::OpenRead($Path)
        $FileName = [System.IO.Path]::GetFileName($Path)
        $FileContent = [System.Net.Http.StreamContent]::new($FileStream)
        $MultipartFormData.Add($FileContent, $Name, $FileName)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM