8

PHPでcUrlを使用して、外部サービスからリクエストしています。

興味深いことに、サーバーはバイナリファイルデータではなく、生の「multipart/form-data」で応答しています。

私のウェブサイトは共有ホスティングを使用しているため、PECLHTTPはオプションではありません。

このデータをPHPで解析する方法はありますか?

サンプルコード:

$response = curl_exec($cUrl);

/* $response is raw "multipart/form-data" string

   --MIMEBoundaryurn_uuid_DDF2A2C71485B8C94C135176149950475371
   Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
   Content-Transfer-Encoding: binary

   (xml data goes here)

   --MIMEBoundaryurn_uuid_DDF2A2C71485B8C94C135176149950475371
   Content-Type: application/zip
   Content-Transfer-Encoding: binary

   (binary file data goes here)

*/

編集:ローカルホストHTTPリクエストへの応答をパイプ処理しようとしましたが、応答データがPHPプロセスで許可されているメモリサイズを超える可能性があります。mem制限を消費することはあまり実用的ではありません。このアクションは、サーバーのパフォーマンスを劇的に低下させます。

元の質問に代わるものがない場合は、PHPのストリームに関して、XML解析とともに非常に大きなPOST要求を処理する方法を提案できます。

これは難しいと思いますので、コメントしてください。私は議論を開いています。

4

5 に答える 5

4

応答からzipファイルが必要な場合は、tmpファイルを記述してcurl応答を保存し、回避策としてストリーミングすることができると思います。マルチパートcurlで試したことはありませんが、機能するはずです。

$fh = fopen('/tmp/foo', 'w'); 
$cUrl = curl_init('http://example.com/foo'); 
curl_setopt($cUrl, CURLOPT_FILE, $fh); // redirect output to filehandle
curl_exec($cUrl); 
curl_close($cUrl);
fclose($fh); // close filehandle or the file will be corrupted

応答のxml部分以外は必要ない場合は、ヘッダーを無効にすることをお勧めします

curl_setopt($cUrl, CURLOPT_HEADER, FALSE);

応答としてxmlのみを受け入れるオプションを追加します

curl_setopt($cUrl, CURLOPT_HTTPHEADER, array('Accept: application/xml'));
//That's a workaround since there is no available curl option to do so but http allows that

[編集]

暗闇の中でのショット...これらのcurlopt設定でテストして、これらを変更することが何か役立つかどうかを確認できますか

$headers = array (
    'Content-Type: multipart/form-data; boundary=' . $boundary,
    'Content-Length: ' . strlen($requestBody),
    'X-EBAY-API-COMPATIBILITY-LEVEL: ' . $compatLevel,  // API version
    'X-EBAY-API-DEV-NAME: ' . $devID,
    'X-EBAY-API-APP-NAME: ' . $appID,
    'X-EBAY-API-CERT-NAME: ' . $certID,
    'X-EBAY-API-CALL-NAME: ' . $verb,
    'X-EBAY-API-SITEID: ' . $siteID, 
    );

$cUrl = curl_init();
curl_setopt($cUrl, CURLOPT_URL, $serverUrl);
curl_setopt($cUrl, CURLOPT_TIMEOUT, 30 );
curl_setopt($cUrl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($cUrl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($cUrl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($cUrl, CURLOPT_POST, 1);
curl_setopt($cUrl, CURLOPT_POSTFIELDS, $requestBody);
curl_setopt($cUrl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($cUrl, CURLOPT_FAILONERROR, 0 );
curl_setopt($cUrl, CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt($cUrl, CURLOPT_HEADER, 0 );
curl_setopt($cUrl, CURLOPT_USERAGENT, 'ebatns;xmlstyle;1.0' );
curl_setopt($cUrl, CURLOPT_HTTP_VERSION, 1 );      // HTTP version must be 1.0
$response = curl_exec($cUrl);

if ( !$response ) {
    print "curl error " . curl_errno($cUrl ) . PHP_EOL;
}
curl_close($cUrl);

[編集II]

前述のように、これは単なる試みであり、カールしたページをマルチパートフォームデータで応答させることはできません。だからここで私に優しくしてください;)

$content_type = ""; //use last know content-type as a trigger
$tmp_cnt_file = "tmp/tmpfile";
$xml_response = ""; // this will hold the "usable" curl response
$hidx = 0; //header index.. counting the number of different headers received

function read_header($cUrl, $string)// this will be called once for every line of each header received
{ 
    global $content_type, $hidx;
    $length = strlen($string);
    if (preg_match('/Content-Type:(.*)/', $string, $match))
    {
        $content_type = $match[1];
        $hidx++;
    }
    /* 
    should set  $content_type to 'application/xop+xml; charset=utf-8; type="text/xml"' for the first 
    and to 'application/zip' for the second response body   

    echo "Header: $string<br />\n";
    */
    return $length;
}

function read_body($cUrl, $string)
{
    global $content_header, $xml_response, $tmp_cnt_file, $hidx;
    $length = strlen($string);
    if(stripos ( $content_type , "xml") !== false)
        $xml_response .= $string;
    elseif(stripos ($content_type, "zip") !== false)
    {
        $handle = fopen($tmp_cnt_file."-".$hidx.".zip", "a");
        fwrite($handle, $string);
        fclose($handle);
    }
    /*
    elseif {...} else{...}
    depending on your needs

    echo "Received $length bytes<br />\n";
    */
    return $length;
}

そしてもちろん、適切なcurloptsを設定します

// Set callback function for header
curl_setopt($cUrl, CURLOPT_HEADERFUNCTION, 'read_header');
// Set callback function for body
curl_setopt($cUrl, CURLOPT_WRITEFUNCTION, 'read_body');

メモリの問題があるため、curl応答を変数に保存しないことを忘れないでください。とにかく、必要なのは上記の$xml_responseだけです。

//$response = curl_exec($cUrl);
curl_exec($cUrl);

また、コードを解析するために、このシナリオで$xml_response開始して作成した一時ファイルを参照できます。tmp/tmpfile-2繰り返しになりますが、上記のコードをテストすることはできませんでした。したがって、これは機能しない可能性があります(ただし、imho;))

[編集III]

curlがすべての着信データを別の(発信)ストリーム(この場合はソケット接続)に直接書き込むようにするとします。

これほど簡単かどうかはわかりません。

$fs = fsockopen($host, $port, $errno, $errstr);
$cUrl = curl_init('http://example.com/foo'); 
curl_setopt($cUrl, CURLOPT_FILE, $fs); // redirect output to sockethandle
curl_exec($cUrl); 
curl_close($cUrl);
fclose($fs); // close handle

それ以外の場合は、既知の書き込み関数とヘッダー関数を少しのトリックで使用する必要があります

//first open the socket (before initiating curl)
$fs = fsockopen($host, $port, $errno, $errstr);
// now for the new callback function
function socket_pipe($cUrl, $string)
{ 
    global $fs;
    $length = strlen($string);
    fputs($fs, $string); // add NOTHING to the received line just send it to $fs; that was easy wasn't it?
    return $length;
}
// and of course for the CURLOPT part
// Set callback function for header
curl_setopt($cUrl, CURLOPT_HEADERFUNCTION, 'socket_pipe');
// Set the same callback function for body
curl_setopt($cUrl, CURLOPT_WRITEFUNCTION, 'socket_pipe');

// do not forget to 
fclose($fs); //when we're done

重要なのは、結果を編集せずに単にパイプするだけで$fs、apacheが特定のポートでリッスンしている必要があり、そのポートにスクリプトを割り当てる必要があります。または、直後に1つのヘッダー行を追加する必要がありますfsockopen

fputs($fp, "POST $path HTTP/1.0\n"); //where path is your script of course
于 2013-02-10T13:32:18.747 に答える
0

申し訳ありませんが、コードをあまり入力しなかったため、あまり役に立ちませんが、curl_setoptオプションで遊んでいたときに同様の問題が発生していたことを覚えています。

CURLOPT_BINARYTRANSFERを使用しましたか?

PHPのドキュメント-> CURLOPT_BINARYTRANSFER- >TRUEから、CURLOPT_RETURNTRANSFERが使用されている場合に生の出力を返します。

于 2012-11-06T09:48:19.540 に答える
0

CURLOPT_RETURNTRANSFERCURLOPT_POSTを設定するだけです

        $c = curl_init($url);
        curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 1);
        curl_setopt($c, CURLOPT_TIMEOUT, 1);
        curl_setopt($c, CURLOPT_POST, 1);
        curl_setopt($c, CURLOPT_POSTFIELDS,
                    array());
        $rst_str = curl_exec($c);
        curl_close($c);
于 2013-02-13T13:56:53.970 に答える
0

このようなことをしてバイナリデータを再構築できます。お役に立てば幸いです。

$file_array = explode("\n\r", $file, 2);
$header_array = explode("\n", $file_array[0]);
foreach($header_array as $header_value) {
  $header_pieces = explode(':', $header_value);
  if(count($header_pieces) == 2) {
    $headers[$header_pieces[0]] = trim($header_pieces[1]);
  }
}
header('Content-type: ' . $headers['Content-Type']);
header('Content-Disposition: ' . $headers['Content-Disposition']);
echo substr($file_array[1], 1);
于 2013-02-13T21:07:07.737 に答える
0

バイナリデータが必要ない場合は、以下を試してみましたか?

curl_setopt($c, CURLOPT_NOBODY, true);
于 2013-02-14T00:01:21.973 に答える