对于我的一个客户,我正在处理从他自己的系统到 DirectAdmin 的 API 连接,用于多种目的。一是管理备份。创建备份后,它也应该可供下载。
但是,我无法让它工作。我正在使用 DirectAdmin API - 使用 HTTPSocket - 使用以下代码构建请求:
<?php
function query( $request, $content = '', $doSpeedCheck = 0 )
{
$this->error = $this->warn = array();
$this->result_status_code = NULL;
// is our request a http:// ... ?
if (preg_match('!^http://!i',$request))
{
$location = parse_url($request);
$this->connect($location['host'],$location['port']);
$this->set_login($location['user'],$location['pass']);
$request = $location['path'];
$content = $location['query'];
if ( strlen($request) < 1 )
{
$request = '/';
}
}
$array_headers = array(
'User-Agent' => "HTTPSocket/$this->version",
'Host' => ( $this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port" ),
'Accept' => '*/*',
'Connection' => 'Close' );
foreach ( $this->extra_headers as $key => $value )
{
$array_headers[$key] = $value;
}
$this->result = $this->result_header = $this->result_body = '';
// was content sent as an array? if so, turn it into a string
if (is_array($content))
{
$pairs = array();
foreach ( $content as $key => $value )
{
$pairs[] = "$key=".urlencode($value);
}
$content = join('&',$pairs);
unset($pairs);
}
$OK = TRUE;
// instance connection
if ($this->bind_host)
{
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket,$this->bind_host);
if (!@socket_connect($socket,$this->remote_host,$this->remote_port))
{
$OK = FALSE;
}
}
else
{
$socket = @fsockopen( $this->remote_host, $this->remote_port, $sock_errno, $sock_errstr, 10 );
}
if ( !$socket || !$OK )
{
$this->error[] = "Can't create socket connection to $this->remote_host:$this->remote_port.";
return 0;
}
// if we have a username and password, add the header
if ( isset($this->remote_uname) && isset($this->remote_passwd) )
{
$array_headers['Authorization'] = 'Basic '.base64_encode("$this->remote_uname:$this->remote_passwd");
}
// for DA skins: if $this->remote_passwd is NULL, try to use the login key system
if ( isset($this->remote_uname) && $this->remote_passwd == NULL )
{
$array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}";
}
// if method is POST, add content length & type headers
if ( $this->method == 'POST' )
{
$array_headers['Content-type'] = 'application/x-www-form-urlencoded';
$array_headers['Content-length'] = strlen($content);
}
// else method is GET or HEAD. we don't support anything else right now.
else
{
if ($content)
{
$request .= "?$content";
}
}
// prepare query
$query = "$this->method $request HTTP/1.0\r\n";
foreach ( $array_headers as $key => $value )
{
$query .= "$key: $value\r\n";
}
$query .= "\r\n";
// if POST we need to append our content
if ( $this->method == 'POST' && $content )
{
$query .= "$content\r\n\r\n";
}
// query connection
if ($this->bind_host)
{
socket_write($socket,$query);
// now load results
while ( $out = socket_read($socket,2048) )
{
$this->result .= $out;
}
}
else
{
fwrite( $socket, $query, strlen($query) );
// now load results
$this->lastTransferSpeed = 0;
$status = socket_get_status($socket);
$startTime = time();
$length = 0;
$prevSecond = 0;
while ( !feof($socket) && !$status['timed_out'] )
{
$chunk = fgets($socket,1024);
$length += strlen($chunk);
$this->result .= $chunk;
$elapsedTime = time() - $startTime;
if ( $elapsedTime > 0 )
{
$this->lastTransferSpeed = ($length/1024)/$elapsedTime;
}
if ( $doSpeedCheck > 0 && $elapsedTime > 5 && $this->lastTransferSpeed < $doSpeedCheck )
{
$this->warn[] = "kB/s for last 5 seconds is below 50 kB/s (~".( ($length/1024)/$elapsedTime )."), dropping connection...";
$this->result_status_code = 503;
break;
}
}
if ( $this->lastTransferSpeed == 0 )
{
$this->lastTransferSpeed = $length/1024;
}
}
list($this->result_header,$this->result_body) = preg_split("/\r\n\r\n/",$this->result,2);
if ($this->bind_host)
{
socket_close($socket);
}
else
{
fclose($socket);
}
$this->query_cache[] = $query;
$headers = $this->fetch_header();
// what return status did we get?
if (!$this->result_status_code)
{
preg_match("#HTTP/1\.. (\d+)#",$headers[0],$matches);
$this->result_status_code = $matches[1];
}
// did we get the full file?
if ( !empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body) )
{
$this->result_status_code = 206;
}
// now, if we're being passed a location header, should we follow it?
if ($this->doFollowLocationHeader)
{
if (!empty($headers['location']))
{
$this->redirectURL = $headers['location'];
$this->query($headers['location']);
}
}
}
?>
我的要求,像这样工作:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
$apiSocket->query('/' . $action, (array) $args);
exit;
当我使用此代码下载一个大小为 840.46M 的文件时,我收到一个(几乎)空的 .zip 文件。当我删除 Content-type 标头时,显示错误:
致命错误:允许的内存大小为 134217728 字节已用尽(尝试分配 133169436 字节)
当然,这是一个逻辑错误,因为套接字试图将所有内容保存到一个字符串中,这变得太大了。所以我将这些行更改为echo's,但结果几乎是正确的大小,但 zip 不可读。这可能是包含标题的文件的结果,该文件曾经使用以下表达式进行拆分:
/\r\n\r\n/
所以,我的问题是,我怎样才能实现这个目标?一个很好的解决方案是 - 我认为 - 删除标题,但是当不将字符串保存到变量时,如何做到这一点?
希望得到答案。
扬·威廉