ftp_alloc() 函数需要启用FTP扩展。在使用前请确保已安装并启用FTP扩展。
ftp_alloc() 函数用于在FTP服务器上为要上传的文件预分配空间。
这个函数向FTP服务器发送一个 ALLO 命令,请求为即将上传的文件分配指定大小的空间。这对于确保服务器有足够空间来接收大文件特别有用。
ftp_alloc(resource $ftp_stream, int $filesize, string &$result = null): bool
| 参数 | 描述 |
|---|---|
$ftp_stream |
FTP连接资源。由 ftp_connect() 或 ftp_ssl_connect() 函数返回的连接标识符。 |
$filesize |
文件大小(以字节为单位)。要预分配的空间大小。 |
&$result |
服务器响应(可选)。如果提供这个变量,服务器返回的文本响应将被存储在其中。 |
truefalseALLO 命令,函数可能会失败<?php
// 连接到FTP服务器
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";
$conn_id = ftp_connect($ftp_server);
if (!$conn_id) {
die("无法连接到 $ftp_server");
}
// 登录到FTP服务器
if (@ftp_login($conn_id, $ftp_user, $ftp_pass)) {
echo "已连接到 $ftp_server,用户为 $ftp_user<br>";
} else {
echo "无法以 $ftp_user 身份登录<br>";
ftp_close($conn_id);
exit;
}
// 启用被动模式(可选,根据服务器配置)
ftp_pasv($conn_id, true);
// 要上传的文件大小(例如:10MB)
$file_size = 10 * 1024 * 1024; // 10MB
// 尝试预分配空间
if (ftp_alloc($conn_id, $file_size, $result)) {
echo "成功预分配了 $file_size 字节的空间<br>";
if ($result) {
echo "服务器响应: " . htmlspecialchars($result) . "<br>";
}
// 现在可以上传文件了
$local_file = "large_file.zip";
$remote_file = "uploads/large_file.zip";
if (ftp_put($conn_id, $remote_file, $local_file, FTP_BINARY)) {
echo "文件上传成功: $local_file -> $remote_file<br>";
} else {
echo "文件上传失败<br>";
}
} else {
echo "预分配空间失败<br>";
if (isset($result)) {
echo "服务器响应: " . htmlspecialchars($result) . "<br>";
}
echo "FTP服务器可能不支持 ALLO 命令,尝试直接上传...<br>";
// 如果不支持预分配,尝试直接上传
$local_file = "large_file.zip";
$remote_file = "uploads/large_file.zip";
if (ftp_put($conn_id, $remote_file, $local_file, FTP_BINARY)) {
echo "文件上传成功(未预分配)<br>";
} else {
echo "文件上传失败<br>";
}
}
// 关闭FTP连接
ftp_close($conn_id);
?>
<?php
/**
* 安全上传大文件到FTP服务器
* @param string $server FTP服务器地址
* @param string $username 用户名
* @param string $password 密码
* @param string $local_file 本地文件路径
* @param string $remote_file 远程文件路径
* @param bool $use_allocation 是否使用预分配
* @return array 上传结果
*/
function ftp_upload_large_file($server, $username, $password, $local_file, $remote_file, $use_allocation = true) {
$result = [
'success' => false,
'message' => '',
'server_response' => ''
];
// 验证本地文件是否存在
if (!file_exists($local_file)) {
$result['message'] = "本地文件不存在: $local_file";
return $result;
}
// 获取文件大小
$file_size = filesize($local_file);
if ($file_size === false) {
$result['message'] = "无法获取文件大小";
return $result;
}
// 连接到FTP服务器
$conn_id = @ftp_connect($server);
if (!$conn_id) {
$result['message'] = "无法连接到FTP服务器: $server";
return $result;
}
// 登录
if (!@ftp_login($conn_id, $username, $password)) {
$result['message'] = "FTP登录失败";
ftp_close($conn_id);
return $result;
}
// 启用被动模式(提高通过防火墙的连接成功率)
ftp_pasv($conn_id, true);
// 如果启用预分配且文件较大(大于1MB)
if ($use_allocation && $file_size > 1024 * 1024) {
$allocation_result = '';
if (ftp_alloc($conn_id, $file_size, $allocation_result)) {
$result['server_response'] = $allocation_result;
$result['message'] .= "已预分配 " . format_bytes($file_size) . " 空间<br>";
} else {
// 预分配失败,记录响应但不中断上传
$result['server_response'] = $allocation_result;
$result['message'] .= "预分配失败,服务器可能不支持此功能<br>";
}
}
// 上传文件
if (@ftp_put($conn_id, $remote_file, $local_file, FTP_BINARY)) {
$result['success'] = true;
$result['message'] .= "文件上传成功: " . basename($local_file) . " → $remote_file";
} else {
$result['message'] .= "文件上传失败";
}
// 关闭连接
ftp_close($conn_id);
return $result;
}
/**
* 格式化字节数为易读格式
*/
function format_bytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
// 使用示例
echo "<h4>大文件上传示例:</h4>";
// 模拟上传
$upload_result = ftp_upload_large_file(
"ftp.example.com",
"username",
"password",
"/path/to/large_file.zip",
"/uploads/large_file.zip",
true // 启用预分配
);
// 显示结果
if ($upload_result['success']) {
echo "<div class='alert alert-success'>";
echo "<strong>上传成功!</strong><br>";
echo $upload_result['message'];
} else {
echo "<div class='alert alert-danger'>";
echo "<strong>上传失败!</strong><br>";
echo $upload_result['message'];
}
if (!empty($upload_result['server_response'])) {
echo "<br><strong>服务器响应:</strong> " .
htmlspecialchars($upload_result['server_response']);
}
echo "</div>";
?>
<?php
/**
* 检查FTP服务器是否支持 ALLO 命令
* @param resource $ftp_conn FTP连接资源
* @return bool 是否支持预分配
*/
function ftp_supports_allocation($ftp_conn) {
// 尝试发送一个小的ALLOC请求(1字节)
$test_size = 1;
$server_response = '';
// 保存原始错误报告级别
$old_error_level = error_reporting(0);
$result = @ftp_alloc($ftp_conn, $test_size, $server_response);
// 恢复错误报告级别
error_reporting($old_error_level);
// 如果返回true,说明服务器支持
if ($result === true) {
return true;
}
// 检查服务器响应中是否包含特定消息
$negative_responses = [
'502', // Command not implemented
'504', // Command not implemented for that parameter
'550', // Requested action not taken
];
foreach ($negative_responses as $response) {
if (strpos($server_response, $response) !== false) {
return false;
}
}
// 如果服务器返回其他响应,可能支持但空间不足等
// 这种情况下我们认为服务器支持 ALLO 命令
return !empty($server_response);
}
// 使用示例
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";
// 连接到FTP服务器
$conn_id = ftp_connect($ftp_server);
if (!$conn_id) {
die("无法连接到 $ftp_server");
}
// 登录
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
die("登录失败");
}
echo "<h4>检查FTP服务器功能:</h4>";
// 检查是否支持 ALLO 命令
if (ftp_supports_allocation($conn_id)) {
echo "<span class='text-success'>✓ FTP服务器支持预分配空间(ALLO命令)</span><br>";
} else {
echo "<span class='text-warning'>✗ FTP服务器不支持预分配空间(ALLO命令)</span><br>";
}
// 检查其他常用命令支持
echo "<br><strong>其他FTP功能检查:</strong><br>";
// 检查是否支持被动模式
ftp_pasv($conn_id, true);
echo "✓ 支持被动模式<br>";
// 尝试获取系统类型(SYST命令)
$syst = ftp_systype($conn_id);
if ($syst !== false) {
echo "✓ 系统类型: " . htmlspecialchars($syst) . "<br>";
} else {
echo "✗ 无法获取系统类型<br>";
}
// 尝试获取当前目录(PWD命令)
$pwd = ftp_pwd($conn_id);
if ($pwd !== false) {
echo "✓ 当前目录: " . htmlspecialchars($pwd) . "<br>";
} else {
echo "✗ 无法获取当前目录<br>";
}
// 关闭连接
ftp_close($conn_id);
?>
<?php
/**
* 安全上传文件,处理空间不足的情况
*/
function ftp_safe_upload($ftp_server, $ftp_user, $ftp_pass, $local_file, $remote_path) {
// 连接和登录
$conn_id = ftp_connect($ftp_server);
if (!$conn_id) {
return ['success' => false, 'error' => '连接失败'];
}
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
ftp_close($conn_id);
return ['success' => false, 'error' => '登录失败'];
}
ftp_pasv($conn_id, true);
$file_size = filesize($local_file);
$file_name = basename($local_file);
$remote_file = rtrim($remote_path, '/') . '/' . $file_name;
// 第一步:尝试预分配空间
$allocation_result = '';
if (ftp_alloc($conn_id, $file_size, $allocation_result)) {
// 预分配成功
echo "<div class='alert alert-success'>";
echo "已成功预分配 " . format_filesize($file_size) . " 空间<br>";
echo "服务器响应: " . htmlspecialchars($allocation_result) . "<br>";
echo "</div>";
} else {
// 预分配失败,分析原因
echo "<div class='alert alert-warning'>";
echo "预分配请求失败<br>";
if (strpos($allocation_result, '550') !== false) {
// 550错误通常表示空间不足或权限问题
echo "<strong>可能原因:</strong><br>";
echo "1. FTP服务器空间不足<br>";
echo "2. 没有写入权限<br>";
echo "3. 文件已存在且不可覆盖<br><br>";
// 尝试检查剩余空间
$free_space = ftp_check_space($conn_id, $remote_path);
if ($free_space !== false) {
echo "可用空间: " . format_filesize($free_space) . "<br>";
echo "所需空间: " . format_filesize($file_size) . "<br>";
if ($free_space < $file_size) {
ftp_close($conn_id);
return [
'success' => false,
'error' => '服务器空间不足。需要: ' .
format_filesize($file_size) .
',可用: ' . format_filesize($free_space)
];
}
}
}
echo "服务器响应: " . htmlspecialchars($allocation_result) . "<br>";
echo "</div>";
}
// 第二步:执行上传
echo "<div class='alert alert-info'>开始上传文件...</div>";
$upload_start = microtime(true);
if (ftp_put($conn_id, $remote_file, $local_file, FTP_BINARY)) {
$upload_time = microtime(true) - $upload_start;
echo "<div class='alert alert-success'>";
echo "<strong>文件上传成功!</strong><br>";
echo "文件名: $file_name<br>";
echo "文件大小: " . format_filesize($file_size) . "<br>";
echo "上传时间: " . round($upload_time, 2) . " 秒<br>";
echo "传输速度: " . format_filesize($file_size / $upload_time) . "/秒<br>";
echo "</div>";
ftp_close($conn_id);
return ['success' => true, 'file' => $remote_file, 'size' => $file_size];
} else {
echo "<div class='alert alert-danger'>";
echo "<strong>文件上传失败!</strong><br>";
echo "可能的原因:<br>";
echo "1. 连接中断<br>";
echo "2. 权限不足<br>";
echo "3. 服务器空间不足<br>";
echo "</div>";
ftp_close($conn_id);
return ['success' => false, 'error' => '上传失败'];
}
}
/**
* 估算FTP服务器的可用空间(使用SIZE命令检查现有文件)
* 注意:这不是标准FTP功能,某些服务器可能不支持
*/
function ftp_check_space($ftp_conn, $path) {
// 尝试获取目录列表来估算空间
// 这是一个简化的示例,实际实现可能更复杂
$list = ftp_nlist($ftp_conn, $path);
if ($list === false) {
return false;
}
// 这里只是示例,实际中需要更复杂的逻辑
// 来获取真实的磁盘空间信息
return 100 * 1024 * 1024; // 假设有100MB空间
}
/**
* 格式化文件大小
*/
function format_filesize($bytes) {
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} elseif ($bytes > 1) {
return $bytes . ' bytes';
} elseif ($bytes == 1) {
return $bytes . ' byte';
} else {
return '0 bytes';
}
}
// 模拟上传(在实际应用中需要真实FTP服务器)
echo "<h4>安全文件上传流程:</h4>";
echo "<p>此示例演示了使用ftp_alloc()进行安全文件上传的完整流程。</p>";
// 注意:在实际运行中需要提供真实的FTP服务器信息
// $result = ftp_safe_upload("server", "user", "pass", "file.txt", "/uploads");
?>
<?php
/**
* 批量上传大文件到FTP服务器
*/
class FTPBatchUploader {
private $conn_id;
private $server;
private $username;
private $password;
private $use_allocation;
private $log = [];
public function __construct($server, $username, $password, $use_allocation = true) {
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->use_allocation = $use_allocation;
}
public function connect() {
$this->conn_id = @ftp_connect($this->server);
if (!$this->conn_id) {
$this->log("无法连接到FTP服务器: {$this->server}");
return false;
}
if (!@ftp_login($this->conn_id, $this->username, $this->password)) {
$this->log("FTP登录失败");
ftp_close($this->conn_id);
return false;
}
ftp_pasv($this->conn_id, true);
$this->log("已连接到FTP服务器: {$this->server}");
return true;
}
public function uploadFiles($files, $remote_base_path) {
if (!$this->conn_id) {
$this->log("未连接到FTP服务器");
return false;
}
$results = [];
$total_size = 0;
$success_count = 0;
$fail_count = 0;
foreach ($files as $local_file => $remote_path) {
$this->log("开始处理: $local_file");
if (!file_exists($local_file)) {
$this->log("文件不存在: $local_file", 'error');
$results[$local_file] = ['success' => false, 'error' => '文件不存在'];
$fail_count++;
continue;
}
$file_size = filesize($local_file);
$remote_file = rtrim($remote_base_path, '/') . '/' . ltrim($remote_path, '/');
// 预分配空间(如果启用)
if ($this->use_allocation && $file_size > 1024 * 1024) {
$alloc_result = '';
if (ftp_alloc($this->conn_id, $file_size, $alloc_result)) {
$this->log("已预分配 " . $this->formatSize($file_size) . " 空间");
} else {
$this->log("预分配失败: " . htmlspecialchars($alloc_result), 'warning');
}
}
// 上传文件
$start_time = microtime(true);
if (ftp_put($this->conn_id, $remote_file, $local_file, FTP_BINARY)) {
$upload_time = microtime(true) - $start_time;
$speed = $file_size / $upload_time;
$this->log("上传成功: " . basename($local_file) .
" (" . $this->formatSize($file_size) .
", " . round($upload_time, 2) . "秒, " .
$this->formatSize($speed) . "/秒)", 'success');
$results[$local_file] = [
'success' => true,
'remote_path' => $remote_file,
'size' => $file_size,
'time' => $upload_time,
'speed' => $speed
];
$total_size += $file_size;
$success_count++;
} else {
$this->log("上传失败: " . basename($local_file), 'error');
$results[$local_file] = ['success' => false, 'error' => '上传失败'];
$fail_count++;
}
}
$this->log("批量上传完成: $success_count 个成功, $fail_count 个失败");
$this->log("总上传大小: " . $this->formatSize($total_size));
return [
'total' => count($files),
'success' => $success_count,
'failed' => $fail_count,
'total_size' => $total_size,
'results' => $results
];
}
public function disconnect() {
if ($this->conn_id) {
ftp_close($this->conn_id);
$this->log("已断开FTP连接");
$this->conn_id = null;
}
}
private function log($message, $type = 'info') {
$timestamp = date('Y-m-d H:i:s');
$log_entry = "[$timestamp] [$type] $message";
$this->log[] = $log_entry;
// 同时输出到页面(在实际应用中可能只记录到日志文件)
$css_class = '';
switch ($type) {
case 'error': $css_class = 'text-danger'; break;
case 'warning': $css_class = 'text-warning'; break;
case 'success': $css_class = 'text-success'; break;
default: $css_class = 'text-info'; break;
}
echo "<div class='$css_class'>$log_entry</div>";
}
private function formatSize($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
if ($bytes == 0) return '0 B';
$i = floor(log($bytes, 1024));
return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
}
public function getLog() {
return $this->log;
}
}
// 使用示例
echo "<h4>批量大文件上传示例:</h4>";
// 模拟文件列表
$files_to_upload = [
'/path/to/large_file1.zip' => 'backups/file1.zip',
'/path/to/large_file2.iso' => 'backups/file2.iso',
'/path/to/document.pdf' => 'docs/document.pdf',
'/path/to/video.mp4' => 'media/video.mp4'
];
// 创建上传器实例
$uploader = new FTPBatchUploader(
"ftp.example.com",
"username",
"password",
true // 启用预分配
);
// 连接服务器
if ($uploader->connect()) {
// 执行批量上传
$results = $uploader->uploadFiles($files_to_upload, '/uploads');
// 显示统计信息
echo "<div class='alert alert-info mt-3'>";
echo "<strong>批量上传统计:</strong><br>";
echo "总文件数: {$results['total']}<br>";
echo "成功: {$results['success']}<br>";
echo "失败: {$results['failed']}<br>";
echo "总大小: " . $uploader->formatSize($results['total_size']) . "<br>";
echo "</div>";
// 断开连接
$uploader->disconnect();
} else {
echo "<div class='alert alert-danger'>无法连接到FTP服务器</div>";
}
?>
ALLO 命令,使用前应检查服务器兼容性--enable-ftp)| 函数 | 描述 |
|---|---|
ftp_connect() |
建立FTP连接 |
ftp_ssl_connect() |
建立安全的FTP over SSL连接 |
ftp_login() |
登录FTP服务器 |
ftp_put() |
上传文件到FTP服务器 |
ftp_get() |
从FTP服务器下载文件 |
ftp_nb_put() |
非阻塞方式上传文件 |
ftp_size() |
获取远程文件大小 |
ftp_pasv() |
启用或禁用被动模式 |
ftp_close() |
关闭FTP连接 |
ALLO 命令ftp_alloc() 的返回值并处理失败情况该函数是 PHP 后端函数,与浏览器无关。需要 PHP 4.0 或更高版本,且启用 FTP 扩展。