Vic*_*ary 6 php curl http-post
我在PHP中使用cUrl从一些外部服务请求.
有趣的是,服务器正在响应原始的"multipart/form-data"而不是二进制文件数据.
我的网站使用共享主机,因此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)
*/
Run Code Online (Sandbox Code Playgroud)
编辑:我尝试将响应传递给localhost HTTP请求,但响应数据可能超过PHP进程中允许的内存大小.扩展内存限制不是很实用,这个操作也大大降低了服务器性能.
如果没有原始问题的替代方法,您可以建议一种方法来处理非常大的POST请求,以及XML解析,就PHP中的流而言.
我知道这很难,请评论.我愿意讨论.
如果您需要响应中的 zip 文件,我想您可以编写一个 tmp 文件来保存卷曲响应,并将其流式传输作为解决方法:从未尝试过使用多部分卷曲,但我想它应该可以工作。
$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
Run Code Online (Sandbox Code Playgroud)
如果除了响应的 xml 部分之外不需要任何内容,您可能需要禁用标头
curl_setopt($cUrl, CURLOPT_HEADER, FALSE);
Run Code Online (Sandbox Code Playgroud)
并添加选项以仅接受 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
Run Code Online (Sandbox Code Playgroud)
黑暗中的一击......你可以用这些curopt设置进行测试,看看修改这些设置是否有帮助
$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);
Run Code Online (Sandbox Code Playgroud)
这只是一次尝试,正如前面提到的,我无法让我的卷曲页面响应多部分表单数据。所以在这里对我温柔一些;)
$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;
}
Run Code Online (Sandbox Code Playgroud)
当然,设置适当的卷发
// Set callback function for header
curl_setopt($cUrl, CURLOPT_HEADERFUNCTION, 'read_header');
// Set callback function for body
curl_setopt($cUrl, CURLOPT_WRITEFUNCTION, 'read_body');
Run Code Online (Sandbox Code Playgroud)
由于内存问题,不要忘记不要将curl响应保存到变量,希望您所需要的一切都在上面的$xml_response中。
//$response = curl_exec($cUrl);
curl_exec($cUrl);
Run Code Online (Sandbox Code Playgroud)
为了解析您的代码,您可以参考您在此场景中$xml_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
Run Code Online (Sandbox Code Playgroud)
否则我们将不得不使用我们已知的写入和标头函数,只需一个小技巧
//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
Run Code Online (Sandbox Code Playgroud)
问题是,不编辑结果并简单地通过管道将其发送到$fs将使得 apache 必须侦听某个端口,然后将脚本分配给该端口。或者您需要在之后直接添加一个标题行fsockopen
fputs($fp, "POST $path HTTP/1.0\n"); //where path is your script of course
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2976 次 |
| 最近记录: |