2012-11-01 13 views
6

私はPHPでcUrlを使用して外部サービスからリクエストしています。PHPでPOSTデータをcUrlから読み取る

興味深いことに、サーバーはバイナリファイルデータではなく、生の "マルチパート/フォームデータ"で応答しています。

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

このデータを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) 

*/ 

EDIT:私はローカルホストHTTP要求に対する応答を配管試みたが、応答データは、PHPプロセスで許容されるメモリサイズを超過する可能性があります。 mem制限を延期することはあまり実用的ではありません。このアクションによって、サーバーのパフォーマンスが大幅に低下します。

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

これは難しいと思いますが、ご意見ください。私は議論のために開いています。

+1

おそらく、MIMEメール解析ライブラリを使用できます。 http://stackoverflow.com/questions/1238642/how-to-extract-mail-atachment-with-php/1240048#1240048 – Barmar

+0

これは純粋なフォームデータです。メールヘッダーは含まれていませんが、試してみる。実際に試してみる前に、私はこれをソリューションに非常に近いと考えています。 – Vicary

+0

私は事務所でコードを取得する前に、旧正月が終わる前に賞金が終了するので、コメントを頂きました。 – Vicary

答えて

4

レスポンスからzipファイルが必要な場合curlレスポンスを保存するためのtmpファイルを作成し、回避策としてストリームするといいでしょう: マルチパートのカールでこれを試したことはありませんが、動作するはずです。あなたは、応答のXML部分以外のものを必要としない場合

$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 

あなただけの応答

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 

としてXMLを受け入れるために、ヘッダー
curl_setopt($cUrl, CURLOPT_HEADER, FALSE); 

を無効にし、オプションを追加したい場合があります[編集]

暗闇の中でのショット... これらでテストできますか?変性するが、これらは何

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

を助けるかどうかを確認するためにcurlopt設定[EDIT II]

述べたように、これは私が私のカールページがマルチパートフォームデータで応答することができません、ただ試しています。だからここに私と一緒に優しくして、適切な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'); 

は うまくいけば、理由はメモリの問題の変数にカール応答を保存しないことを忘れないでください設定)

$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; 
} 

そしてもちろんあなたが必要とするのは、上記の$ xml_responseの中にあります。

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

そして、あなたは$xml_responseを参照すると、このシナリオでtmp/tmpfile-2で始まる作成した一時ファイルができ、あなたのコードを解析するため。繰り返しますが、上記のコードをどのような方法でもテストすることはできませんでした。 (それは私見べき;)だから、これは動作しない場合があります

我々はこのような場合には、ソケット接続を別の(送信)ストリームに直接すべての着信データを書き込むことがカールたいと

[IIIは、EDIT])

私はそれがこのように簡単であるかどうかわからないんだけど:

$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 
0ほんの少しのトリックと私たちの知ら書き込みやヘッダの機能を使用する必要があります

結果は編集結果ではなく、$fsにパイプするだけで、apacheが特定のポートをリッスンしてスクリプトを割り当てる必要があります。 それとも、

fputs($fp, "POST $path HTTP/1.0\n"); //where path is your script of course 

+0

最初のカールリクエストから得たものは、 'multipart/form-data'の形式であり、レスポンスではめったに使用されません。ファイルとして送信すると、次のリクエストとPHPでフォームデータを' $ _FILES'ペイロードの解析では何も行いません。 – Vicary

+0

'$ _FILES'と' $ _POST'は同じファイルoakayに保存されています。しかし、fopenでファイルを再度開いて、ファイルハンドラから解析するときに、curlレスポンスではなく、そのファイルを解析するときにPHPのメモリ制限を超えないようにする必要があります。レスポンスからヘッダーとボディーのサイズを読み込んで変数に格納すると、レスポンスファイルの正しいオフセットに簡単にスキップできます。今日私が時間を見つけたら、私は完全な例を掲示するでしょう。 – itsid

+0

これは奇妙なことですが、カールの要求から 'multipart/form-data'バックを得るのは苦労しましたが、できません。フォームアクションが指し示すupload.phpの代わりにupload-form.htmlを巻いても、これを達成するためのあなたの 'CURLOPT_URL' URLを私に教えてもらえませんか? – itsid

0

多くのコードを書いていないので、私は大いに助けになることはできませんが、私はcurl_setoptオプションを使って遊んでいたときに同様の問題を抱えていたことを覚えています。

あなたはを使用しましたか?CURLOPT_BINARYTRANSFER

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

+0

このケースでは、通常multipart/form-data形式でレスポンスを取得していますが、通常はリクエストにのみ使用されます。私はそれが生のままであることを望んでいませんが、代わりに私のコードに物事が入る前に、何らかの解析メカニズムのapacheとPHPが行います。 – Vicary

0

fsockopenがちょうどCURLOPT_RETURNTRANSFER CURLOPT_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); 
0

あなたはこのような何かをやってあなたにバイナリデータを再アセンブルすることができます設定した後、直接ONEヘッダ行を追加する必要がありますが、私はそれが役に立てば幸い。

$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); 
0

バイナリデータが不要な場合は、以下のように試してみましたか?

curl_setopt($c, CURLOPT_NOBODY, true); 
関連する問題