从cUrl读取PHP中的POST数据

从cUrl读取PHP中的POST数据,php,curl,http-post,Php,Curl,Http Post,我在PHP中使用cUrl来请求外部服务 有趣的是,服务器响应的是原始的“多部分/表单数据”,而不是二进制文件数据 我的网站使用共享主机,因此PECL HTTP不是一个选项 有没有办法用PHP解析这些数据 示例代码: $response = curl_exec($cUrl); /* $response is raw "multipart/form-data" string --MIMEBoundaryurn_uuid_DDF2A2C71485B8C94C13517614995047537

我在PHP中使用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)

*/
编辑:我尝试将响应管道化到本地主机HTTP请求,但响应数据可能超过PHP进程中允许的内存大小。扩展mem限制不是很实际,此操作还显著降低了服务器性能

如果没有其他方法可以替代原来的问题,那么您可以建议一种处理非常大的POST请求的方法,以及用PHP中的流进行XML解析的方法


我知道这很难,请评论。我愿意讨论。

很抱歉,我帮不了你什么忙,因为你没有输入太多代码,但我记得我在使用curl_setopt选项时遇到了类似的问题

您是否使用了CURLOPT_BINARYTRANSFER


从php文档->CURLOPT_BINARYTRANSFER->TRUE可在使用CURLOPT_RETURNTRANSFER时返回原始输出

如果您需要响应中的zip文件,我想您可以编写一个tmp文件来保存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);
[编辑二] 这只是一次尝试,正如前面提到的,我无法让我的卷曲页面用多部分表单数据进行响应。所以在这里对我温柔点;)

不要因为内存问题而忘记不保存对变量的curl响应, 希望您所需要的只是上面的$xml\u响应

//$response = curl_exec($cUrl);
curl_exec($cUrl);
要解析代码,您可以参考本场景中的
$xml\u response
和从
tmp/tmpfile-2
开始创建的临时文件。同样,我无法以任何方式测试上述代码。所以这可能不起作用(但应该是这样的)

[编辑三] 假设我们希望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
否则,我们将不得不使用已知的write和header函数,只需一点技巧

//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在某个端口上侦听,然后将脚本分配给该端口。 或者您需要在
fsockopen

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

只需设置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);

您可以通过这样的方式重新组装二进制数据,我希望这会有所帮助

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

如果你不需要二进制数据,你试过下面的方法吗

curl_setopt($c, CURLOPT_NOBODY, true);

情况是,我得到了原始多部分/表单数据格式的响应,它通常只用于请求。我不希望它是原始的,但是apache和PHP在我的代码中使用某种解析机制,也许你可以使用MIME邮件解析库。请看,这是一个纯
表单数据
,不包含邮件标题,但我会尝试一下。在实际尝试之前,我认为这非常接近于一个解决方案。由于赏金将在农历新年结束之前结束,在我有机会在office中获得代码之前结束,因此对该评论进行了评级。唯一明智的方法是通过本地请求将数据传输到另一个PHP脚本。如果用PHP解析数据,性能会很差。如果您以块的形式读取和分派传入的数据,内存使用率应该不会太高。@Andrew我恐怕代码太多了,请在这里把它简化为一个可理解的问题。我从第一个curl请求中得到的格式是
多部分/表单数据
,很少用于响应,将其作为文件发送将在下一个请求中直接将表单数据放入
$\u文件
,PHP在解析有效负载时不会做任何事情。因此
$\u文件
$\u POST
存储在同一个文件oakay中。但是,当您使用fopen再次打开文件并从文件处理程序解析文件时,它仍然应该防止超过php内存限制,而不是curl响应。您可能希望从响应中读取标题和正文大小,并将其存储在变量中,以便轻松跳到响应文件的正确偏移量。如果我今天有时间,我会发布一个完整的例子。好的。。这很奇怪,我试图从curl请求中获取一个
多部分/表单数据
,但我做不到。即使我卷曲upload-form.html而不是表单操作所指向的upload.php,也不行。很抱歉,您是否介意让我知道您的
CURLOPT_URL
URL以实现此目的?进行快速测试不太实际,因为我正在使用易趣api,它需要进行一些设置才能工作。如果您不将响应和
fputs
拆分到socketconnection,它将作为普通请求,它不会添加任何新的标题信息,也不会包装任何内容,它只是一个socketconnection。。。我将在上面解释(代码等)
$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);
curl_setopt($c, CURLOPT_NOBODY, true);