【问题标题】:How to send multipart/form-data with PowerShell Invoke-RestMethod如何使用 PowerShell Invoke-RestMethod 发送 multipart/form-data
【发布时间】:2014-03-18 21:10:00
【问题描述】:

我正在尝试通过 Invoke-RestMethod 在与带有 -F 开关的 curl 类似的上下文中发送文件。

卷曲示例

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

在 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
}

如果我检查提琴手,我会看到正文包含原始二进制数据,但我收到 200 响应,显示没有发送有效负载。

我也尝试过使用 -InFile 参数,但没有成功。

我已经看到了许多使用 .net 类的示例,但我试图使用较新的 Powershell 3 命令来保持这种简单性。

有人对这项工作有任何指导或经验吗?

【问题讨论】:

  • 不支持多部分消息。您可以将内容类型设置为任何内容,但将其设置为multipart/form-data 不会导致消息被格式化为multipart
  • Multipart/form-data 在添加为内容类型时确实有效。我在这里遇到的问题是 API 需要一些参数来接受原始内容,然后我能够使用 -InFile \path\to\file 并使用 Invoke-RestMethod 上传

标签: powershell curl


【解决方案1】:

接受的答案不会执行 multipart/form-data 请求,而是执行 application/x-www-form-urlencoded 请求,将 Content-Type 标头强制为正文不包含的值。

使用 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() }
}

【讨论】:

【解决方案2】:

我找到了这个post 并稍作修改

$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

【讨论】:

    【解决方案3】:

    这里的问题是 API 需要一些额外的参数。初始请求需要一些参数来接受原始内容并指定文件名/大小。设置好并取回正确的提交链接后,我就可以使用了:

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

    【讨论】:

    • 您必须添加哪些附加参数?我在使用 ShareFile api 时遇到了完全相同的问题...
    • 真的取决于,你读过api.sharefile.com/rest/docs/resource.aspx?name=Items 你可以试试这样的: sf/v3/Items($parentFolderId)/Upload?method=standard&raw=true&fileName=$fileName&fileSize=$fileSize 显然替换那些变量与您的数据
    • 我的问题是,这是否特定于您尝试使用的 API,或者这是否是使用分段帖子上传文件的一般要求? JIRA 的API documentation 仅列出了一个带有 curl 的示例(并提到了一些标头要求),没有其他 post 参数。
    • 这不会创建所需的多部分正文格式,正文不会有分隔内容的boundary=------------------------abcdefg1234 部分。见stackoverflow.com/q/25075010/516748
    【解决方案4】:

    对于任何想知道(如 Jelphy)David 的答案是否可以与 cookie/凭据一起使用的人,答案是肯定的。

    首先用 Invoke-WebRequest 设置会话:

    Invoke-WebRequest -Uri "$LoginUri" -Method Get -SessionVariable '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。但这是重要的部分。在创建 HttpClient 之前,创建一个 HttpClientHandler 并使用会话中的 cookie 设置它的 CookieContainer 属性:

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

    然后将此对象传递给HttpClient构造函数

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

    瞧,您现在有了一个通过 Invoke-WebRequest 自动设置会话 cookie 的 HttpClient。大卫的例子的其余部分应该可以工作(为了完整起见,复制到这里):

    $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()
    

    每个请求我都有很多文件要上传,所以我把最后一点分解成一个 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)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-09
      • 1970-01-01
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 2018-04-08
      • 2015-02-04
      • 1970-01-01
      相关资源
      最近更新 更多