0

目前我正在尝试通过接受 PUT 的 REST API 上传文件。我需要提供两件事:描述目标字段的 xml/json 数据和原始数据。此操作的文档可在此处找到:

http://lj.platformatyourservice.com/wiki/REST_API:record_Resource#Multipart_Operations_for_Raw_Data

如果你想跳到这个问题,它就在底部附近。

到目前为止我所拥有的:

public function uploadDocument($aContract){
    $sUrl = $this->sRestUrl."/record/contract/1523";

    $sFileName = TMP_DIR."/".$aContract['Name'];
    $rTmpFile = fopen($sFileName, "w");
    $sContents = fwrite($rTmpFile, $aContract['Content']);

    $aData = array(
        '__json_data__' => '{
            "platform":{
                "record": {
                    "contract_file": "{$aContract[\'Name\']}"
                }
            }
        }',
        'contract_file' => "@$sFileName"
    );

    $ch = curl_init($sUrl);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_VERBOSE, 1);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLINFO_HEADER_OUT, true);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data;'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $aData);

    $rResponse = curl_exec($ch);
    curl_close($ch);

    return $rResponse
}

这几乎已经足够了。它生成这个请求:

PUT https://na.longjump.com/networking/rest/record/contract/1523 HTTP/1.1
Host: username.project
Accept: */*
Cookie: project=2943572094357209345
Content-Length: 304581
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------a9sd7f039h2
------------------------------0849a88a4ca4 Content-Disposition: form-data; name="__json_data__" { "platform":{ "record": { "contract_file": "{$aContract['Name']}" } } } ------------------------------0849a88a4ca4 Content-Disposition: form-data; name="contract_file"; filename="/home/username/project/tmp/document.doc" Content-Type: application/octet-stream

然后是所有原始编码的二进制数据(它确实成功地转换成一个单词文档)。

让我重新格式化标题,以便您可以更轻松地阅读它:

Content-Type: multipart/form-data; boundary=----------------------------a9sd7f039h2
------------------------------a9sd7f039h2
Content-Disposition: form-data;
name="__json_data__"
{ "platform":{ "record": { "contract_file": "{$aContract['Name']}" } } }     
------------------------------a9sd7f039h2
Content-Disposition: form-data; 
name="contract_file"; 
filename="/home/username/project/tmp/document.doc" 
Content-Type: application/octet-stream

这让我得到以下回应:

HTTP/1.1 400 Bad Request Server: Apache-Coyote/1.1 Cache-Control: no-cache 
Pragma: no-cache Content-Type: application/json;charset=UTF-8 P3P: CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" 
Date: Fri, 25 Jan 2013 23:49:36 GMT Transfer-Encoding: chunked Connection: close     
Connection: Transfer-Encoding {"platform": {"message": { "code": "-684", "description": "Invalid Content-Type" }}}

我认为这很好,只是我需要为 .json 的 json 数据设置一个 Content-Type 标头Content-Type: application/json。你是怎样做的?

我已经看到建议这样做:

$aData = array(
    '__json_data__' => '{"data":"data"};type=application/json
);

或者

$aData = array(
    '__json_data__' => '{"data":"data"};Content-Type=application/json
);

但只在一处。我试过了,它并没有真正做任何事情,而且还是很草率。另外,我已经尝试过 http_build_query 的数据,但这也没有为我做。

想法?

4

1 回答 1

0

好的。经过大量的虚假,我想出了一个奇怪的解决方案。基本上,我向自己的服务器发出了 put 请求,将响应回显给自己,然后将其放入 CURLOPT_POSTFIELDS。这是代码(请记住它是简化的;真正的代码在函数中具有第二位,都在一个类中):

function getRawHeader($ch){
    $sResponse = $this->executeCurl($ch);

    preg_match("/------------------------------(\w)+/", $sResponse, $aBoundary);
    if($aBoundary){
        $sBoundary = $aBoundary[0];
        $aResponse = explode($sBoundary, $sResponse);
        foreach($aResponse as &$sPart){
             // Find the string that defines content-type, and add it as a header (if there is one)
            preg_match("/;Content-Type:\s([\w\/])+/", $sPart, $aContentType);
            if($aContentType){
                $sContentType = $aContentType[0];
                // Get that out of the request
                $sPart = str_replace("$sContentType", "", $sPart);
                // Trim the fluff for the content type
                $sContentType = str_replace(";Content-Type: ", "", $sContentType);
                // Now insert the content type into the header.
                $sPart = str_replace(": form-data;", ": form-data; Content-Type: $sContentType;", $sPart);
            }

        }
        unset($sPart);

        return implode($sBoundary, $aResponse);
    }
    return $sResponse;
}


$sLongjumpUrl = $this->sRestUrl."/record/Contract_Custom/1001";
$sLocalUrl = HTML_ROOT."/echorequest.php";

$sFileName = TMP_DIR."/".$aContract['Name'];
$rTmpFile = fopen($sFileName, "w");
$sContents = fwrite($rTmpFile, $aContract['Content']);

$aData = array(
    '__json_data__' => json_encode(
        array(
            'platform' => array(
                'record' => array(
                    'contract_file' => $aContract['Name']
                )
            )
        )
    ).";Content-Type: application/json", // this gets parsed manually below
    'contract_file' => "@$sFileName"
);
// get our raw subheaders
$ch = $this->getCurl($sLocalUrl);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data"));
curl_setopt($ch, CURLOPT_POSTFIELDS, $aData);
$sResponse = $this->getRawHeader($ch);

// Get us a new curl, for actually sending the data to longjump
$ch = $this->getCurl($sLongjumpUrl);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data"));

// We gotta manually add a boundary consistent with the one automatically generated above.
preg_match("/------------------------------(\w)+/", $sResponse, $aBoundary);
$sBoundary = $aBoundary ? $aBoundary[0] : '';
$sBoundaryMarker = $sBoundary ? "boundary=" : '';
curl_setopt($ch, CURLOPT_POSTFIELDS, $sBoundaryMarker.preg_replace("/------------------------------(\w)+/", $sBoundary, $sResponse));

$sResponse = $this->executeCurl($ch);

这似乎已经完成了获得我想要的自定义标题的工作,尽管我对此不是很有信心。我仍然遇到api返回空白字符串的问题,没有状态码,所以我不知道这是怎么回事。但我认为这个问题已经得到解决。

于 2013-01-28T23:51:23.260 回答