如何通过网络请求传递数组

How to pass an array via web-request

情况:

我们从某些网络服务(PowerShell 脚本 运行 每晚自动收集许多报告,并且每天都在手动模式下(在网络表单上拖放) ) 此报告已加载到我们的数据库中。

现在我们的 IT 部门给了我们一个 API 可以在没有用户交互的情况下处理这项工作。

问题:

如求职信中所写(关于此 API),它等待 reports[n] 数组和文件。可以用 PHP 和 curl:

来完成
$report = 'report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip';
$cfile = new CURLFile(realpath($report),'application/zip',$report);
$PostData = array("reports[0]"=>$cfile);

但是如何通过 PowerShell 发送名为 reports[n] 的数组?

我试过的:

$url = "https://test.example.com/uploadAPI/upload.php"
$Source =  "D:\report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"
$contentType = "multipart/form-data"

$Username = "ApiUploadKey"
$Headers = @{Authorization="Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:" -f $Username)))}

$FileContens = get-content $Source
$PostData = @{"reports[0]" = $FileContens;} 
#$reports = @($FileContens,'application/zip',$Source)

(Invoke-WebRequest -uri $url -Method POST -Headers $Headers -Body $PostData -ContentType $contentType).Content

#Invoke-RestMethod -uri $url -Method POST -Headers $Headers -Body $PostData -ContentType $contentType

这给了我一个我正在通过 not-a-report.

的回复

编辑 2016-10-11

进一步调查让我想到这个 answer and this article。我尝试使用 boundary:

clear
$url = "https://test.example.com/uploadAPI/upload.php"
$filename = "report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"
$Source =  "D:\"+$filename

$Username = "ApiUploadKey"
$Headers = @{Authorization="Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:" -f $Username)))}

$FileContens = get-content $Source
$enc = [System.Text.Encoding]::GetEncoding("iso-8859-1")
$fileBin = [IO.File]::ReadAllBytes($Source)
$fileEnc = $enc.GetString($fileBin)


$boundary = [System.Guid]::NewGuid().ToString()

$LF = "`n"

$contentType = "multipart/form-data; boundary=--$boundary"
#$bodyLines = "--"+$boundary+$LF+"Content-Disposition: form-data; name=`"reports[]`"; filename=`""+$filename+"`""+$LF+$LF+"Content-Type: application/zip"+$LF+"--"+$boundary+"--"+$LF+$LF+$FileContens+$LF+"--"+$boundary

$bodyLines = (
    "--$boundary",   #I have tried reports[0] here too
    "Content-Disposition: form-data; name=`"reports[]`"; filename=`"$filename`"",   # filename= is optional
    "Content-Type: application/zip",
    "",
    #$FileContens,
    $fileEnc,
    "--$boundary--"
    ) -join $LF


try {

    #Invoke-WebRequest -Uri "https://asrp.cntd.ru/uploadAPI/" -Headers $Headers -WebSession $ws
    Invoke-RestMethod  -Uri $url -Body $bodyLines -Method POST -Headers $Headers -ContentType $contentType -TimeoutSec 50
}
catch [System.Net.WebException] {
    Write-Error( "FAILED to reach '$url': $_" )
    throw $_
}

但结果相同。

我也试过这个:

$wc = new-object System.Net.WebClient
$wc.Credentials = new-object System.Net.NetworkCredential("ApiUploadKey","")

ls "D:\*.zip" | foreach { 
    $wc.UploadFile('https://test.example.com/uploadAPI/upload.php', $_.FullName )
    write-host $_.FullName
}

还有一个来自这个 answer 的解决方案:

Invoke-RestMethod -Uri $url -InFile $Source -ContentType "multipart/form-data" -Method POST -Headers $Headers

总是相同的响应 - 不是报告

编辑 2016-10-17

curl

我已经为 windows 下载了 curl。并像这样使用它:

curl.exe https://test.example.com/uploadAPI/upload.php --user ApiUploadKey: --form "reports[0]=@d:\report_746_226255_20161010_1635.zip;type=application/zip"

这给了我:

[{"code" : 102 , "guid" : "{23CE9F7F-BEC8-4D4C-8AC3-2865CFA94FBD}" , "id" : "5804902bc73a2475177464", "filename" : "report_746_226255_20161010_1635.zip"}]

所以使用 curl 效果很好!

提琴手

不知道 post 的确切日志。

当我这样发送文件时:

POST https://test.example.com/uploadAPI/upload.php

Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: test.example.com
Authorization: Basic ...
Content-Length: 21075175

请求正文:

---------------------------acebdf13572468
Content-Disposition: form-data; name="reports[]"; filename="report_746_226254_20161010_1320.{B67A9D89-368B-4665-96AC-77C2CA0F4766}.zip"
Content-Type: application/zip

<@INCLUDE *D:\report_746_226254_20161010_1320.{B67A9D89-368B-4665-96AC-77C2CA0F4766}.zip*@>
---------------------------acebdf13572468--

我得到了

HTTP/1.1 200 OK
Date: Mon, 17 Oct 2016 10:04:43 GMT
Server: Apache/2.4.20 (Win64) OpenSSL/1.0.2h PHP/7.0.6
X-Powered-By: PHP/7.0.6
Set-Cookie: SESSION_UPLOAD_ID=.....; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Connection: close
Content-Length: 193
Content-Encoding: none
Accept-Ranges: bytes
Content-Type: text/html; charset=UTF-8

[{"code" : 102 , "guid" : "{B67A9D89-368B-4665-96AC-77C2CA0F4766}" , "id" : "5804a23be4152532018928", "filename" : "report_746_226254_20161010_1320.{B67A9D89-368B-4665-96AC-77C2CA0F4766}.zip"}]

PHP 代码使用 Curl class 并将 $PostData 设置为:

 $PostData = array("reports[0]"=>$cfile);

$PostData 是一个 key/value 对数组(在 PHP 中),键名为 Reports[0]。它只是一个字符串名称,表示键而不是数组元素。

在 curl 命令行工具中,我们可以使用以下命令上传文件:

 curl.exe   -F Reports[0]=@"I:\test\report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"  http://MyServer/uploader/api/upload

或者,它可能是(注意关键 Reports[0][2][3] ):

   curl.exe   -F Reports[0][2][3]=@"I:\test\report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"  http://asd-pc/uploader/api/upload

-F 为输入形式key/value对 注意文件名前的@ 在你的服务器上试一下,在Fiddler中查看session就知道curl和PHP是如何向服务器发送请求的。

对于具有其他键名的其他文件,例如 Reports1.

,您可以传递其他参数,例如 -H (header) -v(详细)-F

我用我的网络服务器 运行 将文件上传到服务器的 Web Api2 服务测试了这段代码,运行 没问题。

所以,问题是:如何将该代码转换为与 Powershell 一起工作。

在 curl PHP 或命令行工具中,它会自动准备 Header 和请求 Body 以支持 Multipart/form-data.

的文件上传

两个 Powershell 命令:Invoke-WebRequest 和 Invoke-RestMethod 都不知道如何格式化请求 body 以符合 Multipart/form-data 中给出的标准RFC Forms: multipart/form-data

您必须手动设置消息 body 然后调用您的调用

以下脚本使用 powershell 将文件上传到网络服务器。

我设置 Content-Type: application/octet-stream 以支持任何类型,包括 zip 文件。

    function Upload-File ( $InFile,$Uri    )
    {    
        $Username = "ApiUploadKey"
        $Headers = @{Authorization="Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:" -f $Username)))}
        $LF = "`n"   
        $fileName = Split-Path $InFile -leaf
        $boundary = [System.Datetime]::Now.Ticks.ToString()
        $binaryData = [System.IO.File]::ReadAllBytes($InFile)   
        $binaryEncoded=[System.Text.Encoding]::GetEncoding("iso-8859-1").GetString($binaryData) 
        $ContentType = "application/octet-stream" 

        $body = @"
    --$boundary
    Content-Disposition: form-data; name=`"Reports[0]`"; filename=`"$fileName`"
    Content-Type: $ContentType
    $LF
    $binaryEncoded
    --$boundary--
    $LF
    "@  

        try
        { 

          return Invoke-RestMethod -Uri $Uri -Method Post -ContentType "multipart/form-data; boundary=$boundary"   -Body $body -Headers $Headers                             

        }
        catch [Exception]
        {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }


    #--   test the function ---------------------------------
    cls
    $uri = "http://MyServer/uploader/api/upload"        
    $filePath = "I:\test\report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"
    $response = Upload-File -InFile $filePath -Uri $uri  #-Credential $credentials
    $response

fiddler

的输出session
    POST http://MyServer/uploader/api/upload HTTP/1.1
    Authorization: Basic QXBpVXBsb2FkS2V5Og==
    User-Agent: Mozilla/5.0 (Windows NT; Windows NT 6.1; en-US) WindowsPowerShell/5.0.10586.117
    Content-Type: multipart/form-data; boundary=636123339963709320
    Host: MyServer
    Content-Length: 379
    Connection: Keep-Alive

    --636123339963709320
    Content-Disposition: form-data; name="Reports[0]"; filename="report_20160825.{8302F59C-E1E4-410F-BE37-A24CCD7E515E}.zip"
    Content-Type: application/octet-stream


    xxxxxxxxxxxxxxxxxxxxxxxxxxxStream of byte sxxxxxxxxxxxxxxx

在上面的 session 数据中,您会看到 Reports[0] 的标题为 name

您可以在命令中使用相同的代码:Invoke-WebRequest