PHP ftp_get() 函数
ftp_get() 函数从FTP服务器下载文件到本地。
注意:与ftp_fget()函数不同,ftp_get()直接将文件下载到指定的本地路径,而不需要预先打开文件句柄。
语法
ftp_get(
resource $ftp_stream,
string $local_file,
string $remote_file,
int $mode = FTP_BINARY,
int $resumepos = 0
): bool
参数说明
| 参数 |
描述 |
$ftp_stream |
必需。FTP连接的资源标识符,由ftp_connect()或ftp_ssl_connect()函数返回。 |
$local_file |
必需。本地文件路径,下载的文件将保存到此路径。 |
$remote_file |
必需。要下载的远程文件的路径。 |
$mode |
可选。传输模式。可以是:
FTP_ASCII - ASCII文本模式
FTP_BINARY - 二进制模式(默认)
|
$resumepos |
可选。远程文件中开始下载的位置(字节偏移量)。默认为0。 |
返回值
示例
示例1:基本使用
从FTP服务器下载文件到本地:
<?php
// FTP服务器信息
$ftp_server = "ftp.example.com";
$ftp_user = "username";
$ftp_pass = "password";
// 建立FTP连接
$conn_id = ftp_connect($ftp_server);
if ($conn_id === false) {
die("无法连接到FTP服务器");
}
// 登录FTP服务器
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
ftp_close($conn_id);
die("FTP登录失败");
}
// 开启被动模式
ftp_pasv($conn_id, true);
// 远程文件和本地文件路径
$remote_file = "public_html/document.pdf";
$local_file = "downloaded_document.pdf";
echo "开始下载文件...\n";
echo "远程文件: $remote_file\n";
echo "保存到: $local_file\n";
// 获取远程文件大小
$remote_size = ftp_size($conn_id, $remote_file);
if ($remote_size == -1) {
echo "远程文件不存在或无法访问\n";
ftp_close($conn_id);
exit(1);
}
echo "远程文件大小: " . formatBytes($remote_size) . "\n";
// 下载文件(二进制模式)
if (ftp_get($conn_id, $local_file, $remote_file, FTP_BINARY)) {
echo "文件下载成功\n";
// 验证本地文件大小
if (file_exists($local_file)) {
$local_size = filesize($local_file);
echo "本地文件大小: " . formatBytes($local_size) . "\n";
if ($local_size == $remote_size) {
echo "文件大小验证通过\n";
} else {
echo "警告: 下载的文件大小不匹配\n";
}
}
} else {
echo "文件下载失败\n";
}
// 关闭FTP连接
ftp_close($conn_id);
// 辅助函数:格式化文件大小
function formatBytes($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];
}
?>
示例2:下载文本文件(ASCII模式)
<?php
/**
* 下载文本文件并使用ASCII模式
*/
function downloadTextFile($ftp_server, $ftp_user, $ftp_pass, $remote_file, $local_file) {
$conn_id = ftp_connect($ftp_server);
if (!$conn_id) {
throw new Exception("无法连接到FTP服务器");
}
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
ftp_close($conn_id);
throw new Exception("FTP登录失败");
}
ftp_pasv($conn_id, true);
// 检查远程文件是否存在
$remote_size = ftp_size($conn_id, $remote_file);
if ($remote_size == -1) {
ftp_close($conn_id);
throw new Exception("远程文件不存在: $remote_file");
}
echo "下载文本文件...\n";
echo "远程: $remote_file\n";
echo "本地: $local_file\n";
echo "大小: " . formatBytes($remote_size) . "\n";
// 使用ASCII模式下载(自动转换行结束符)
if (!ftp_get($conn_id, $local_file, $remote_file, FTP_ASCII)) {
ftp_close($conn_id);
throw new Exception("文件下载失败");
}
// 验证下载的文件
if (file_exists($local_file)) {
$local_size = filesize($local_file);
$content = file_get_contents($local_file);
$line_count = substr_count($content, "\n") + 1;
echo "下载成功\n";
echo "本地文件大小: " . formatBytes($local_size) . "\n";
echo "文件行数: $line_count\n";
echo "字符数: " . strlen($content) . "\n";
// 显示前5行内容
echo "\n文件前5行内容预览:\n";
$lines = explode("\n", $content);
for ($i = 0; $i < min(5, count($lines)); $i++) {
echo " " . ($i + 1) . ": " . htmlspecialchars(substr($lines[$i], 0, 100)) .
(strlen($lines[$i]) > 100 ? "..." : "") . "\n";
}
}
ftp_close($conn_id);
return true;
}
// 使用示例
try {
downloadTextFile(
"ftp.example.com",
"username",
"password",
"public_html/logs/access.log",
"local_access.log"
);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
?>
示例3:断点续传(恢复下载)
<?php
/**
* 支持断点续传的文件下载
*/
function resumeDownloadFile($ftp_server, $ftp_user, $ftp_pass, $remote_file, $local_file) {
$conn_id = ftp_connect($ftp_server);
if (!$conn_id) {
return ["success" => false, "message" => "无法连接到FTP服务器"];
}
if (!ftp_login($conn_id, $ftp_user, $ftp_pass)) {
ftp_close($conn_id);
return ["success" => false, "message" => "FTP登录失败"];
}
ftp_pasv($conn_id, true);
// 检查远程文件
$remote_size = ftp_size($conn_id, $remote_file);
if ($remote_size == -1) {
ftp_close($conn_id);
return ["success" => false, "message" => "远程文件不存在"];
}
// 检查本地文件
$local_size = 0;
if (file_exists($local_file)) {
$local_size = filesize($local_file);
if ($local_size < $remote_size) {
// 部分下载,需要续传
echo "发现部分下载的文件,从 $local_size 字节处继续下载\n";
$resumepos = $local_size;
} elseif ($local_size == $remote_size) {
ftp_close($conn_id);
return ["success" => true, "message" => "文件已完整下载"];
} else {
// 本地文件比远程大,重新下载
unlink($local_file);
$local_size = 0;
$resumepos = 0;
echo "本地文件异常,重新下载\n";
}
} else {
$resumepos = 0;
}
echo "开始下载文件...\n";
echo "远程文件大小: " . formatBytes($remote_size) . "\n";
echo "从位置: " . formatBytes($resumepos) . " 开始\n";
// 执行下载(指定从哪个位置开始)
if (ftp_get($conn_id, $local_file, $remote_file, FTP_BINARY, $resumepos)) {
ftp_close($conn_id);
$final_size = filesize($local_file);
$downloaded = $final_size - $resumepos;
return [
"success" => true,
"message" => "下载完成",
"total_size" => $remote_size,
"downloaded" => $downloaded,
"start_position" => $resumepos,
"local_size" => $final_size
];
} else {
ftp_close($conn_id);
return ["success" => false, "message" => "下载失败"];
}
}
// 使用示例
$result = resumeDownloadFile(
"ftp.example.com",
"username",
"password",
"public_html/large_video.mp4",
"local_video.mp4"
);
if ($result['success']) {
echo "下载成功!\n";
echo "远程文件大小: " . formatBytes($result['total_size']) . "\n";
echo "本次下载: " . formatBytes($result['downloaded']) . "\n";
echo "开始位置: " . formatBytes($result['start_position']) . "\n";
echo "本地文件大小: " . formatBytes($result['local_size']) . "\n";
} else {
echo "下载失败: " . $result['message'] . "\n";
}
?>
示例4:批量下载文件
<?php
/**
* 批量下载文件
*/
class BatchFTPDownloader {
private $conn;
private $download_dir;
private $download_log = [];
public function __construct($host, $username, $password, $download_dir = null) {
$this->conn = ftp_connect($host);
if (!$this->conn) {
throw new Exception("无法连接到FTP服务器");
}
if (!ftp_login($this->conn, $username, $password)) {
ftp_close($this->conn);
throw new Exception("FTP登录失败");
}
ftp_pasv($this->conn, true);
// 设置下载目录
if ($download_dir === null) {
$this->download_dir = sys_get_temp_dir() . '/ftp_downloads/';
} else {
$this->download_dir = rtrim($download_dir, '/') . '/';
}
// 创建下载目录
if (!is_dir($this->download_dir) && !mkdir($this->download_dir, 0755, true)) {
throw new Exception("无法创建下载目录: " . $this->download_dir);
}
}
/**
* 下载单个文件
*/
private function downloadSingleFile($remote_file, $local_file, $mode = FTP_BINARY) {
$start_time = microtime(true);
// 获取远程文件大小
$remote_size = ftp_size($this->conn, $remote_file);
if ($remote_size == -1) {
return [
'success' => false,
'message' => '远程文件不存在',
'remote_file' => $remote_file
];
}
// 执行下载
if (ftp_get($this->conn, $local_file, $remote_file, $mode)) {
$end_time = microtime(true);
$duration = round($end_time - $start_time, 2);
$local_size = filesize($local_file);
return [
'success' => true,
'message' => '下载成功',
'remote_file' => $remote_file,
'local_file' => $local_file,
'remote_size' => $remote_size,
'local_size' => $local_size,
'duration' => $duration,
'speed' => $remote_size > 0 ? round($remote_size / $duration / 1024, 2) . ' KB/s' : '未知'
];
} else {
return [
'success' => false,
'message' => '下载失败',
'remote_file' => $remote_file
];
}
}
/**
* 批量下载文件
*/
public function downloadFiles($remote_files, $local_prefix = '') {
$results = [];
$total_files = count($remote_files);
$success_count = 0;
$total_size = 0;
echo "开始批量下载 {$total_files} 个文件...\n";
foreach ($remote_files as $index => $remote_file) {
$file_num = $index + 1;
echo "[{$file_num}/{$total_files}] 正在下载: {$remote_file}\n";
// 生成本地文件名
$local_file = $this->download_dir . $local_prefix . basename($remote_file);
// 根据文件扩展名选择模式
$mode = $this->getTransferMode($remote_file);
// 下载文件
$result = $this->downloadSingleFile($remote_file, $local_file, $mode);
// 记录结果
$this->download_log[] = $result;
$results[] = $result;
if ($result['success']) {
$success_count++;
$total_size += $result['remote_size'];
echo " 成功: " . formatBytes($result['remote_size']) .
" (" . $result['duration'] . "秒, " . $result['speed'] . ")\n";
} else {
echo " 失败: " . $result['message'] . "\n";
}
}
echo "\n批量下载完成\n";
echo "成功: {$success_count}/{$total_files} 个文件\n";
echo "总大小: " . formatBytes($total_size) . "\n";
echo "下载目录: " . $this->download_dir . "\n";
return $results;
}
/**
* 下载整个目录
*/
public function downloadDirectory($remote_dir, $recursive = false) {
$files = $this->getFileList($remote_dir, $recursive);
if (empty($files)) {
echo "目录中没有文件可下载\n";
return [];
}
return $this->downloadFiles($files);
}
/**
* 获取文件列表
*/
private function getFileList($directory, $recursive = false) {
$files = [];
// 获取目录列表
$items = ftp_nlist($this->conn, $directory);
if ($items === false) {
return [];
}
foreach ($items as $item) {
$remote_path = $directory . '/' . basename($item);
// 跳过当前目录和上级目录
if ($item == '.' || $item == '..') {
continue;
}
// 检查是否为目录
$is_dir = (ftp_size($this->conn, $remote_path) == -1);
if ($is_dir && $recursive) {
// 递归获取子目录文件
$sub_files = $this->getFileList($remote_path, true);
$files = array_merge($files, $sub_files);
} elseif (!$is_dir) {
// 添加文件
$files[] = $remote_path;
}
}
return $files;
}
/**
* 根据文件扩展名选择传输模式
*/
private function getTransferMode($filename) {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$text_extensions = [
'txt', 'html', 'htm', 'php', 'css', 'js',
'json', 'xml', 'csv', 'ini', 'conf', 'log',
'md', 'markdown', 'yml', 'yaml'
];
return in_array($extension, $text_extensions) ? FTP_ASCII : FTP_BINARY;
}
/**
* 获取下载日志
*/
public function getDownloadLog() {
return $this->download_log;
}
/**
* 生成下载报告
*/
public function generateReport() {
$total_files = count($this->download_log);
$successful_files = 0;
$total_size = 0;
$total_time = 0;
foreach ($this->download_log as $log) {
if ($log['success']) {
$successful_files++;
$total_size += $log['remote_size'];
$total_time += $log['duration'];
}
}
$success_rate = $total_files > 0 ? ($successful_files / $total_files * 100) : 0;
$average_speed = $total_time > 0 ? round($total_size / $total_time / 1024, 2) : 0;
return [
'total_files' => $total_files,
'successful_files' => $successful_files,
'success_rate' => round($success_rate, 2) . '%',
'total_size' => formatBytes($total_size),
'total_time' => round($total_time, 2) . '秒',
'average_speed' => $average_speed . ' KB/s',
'download_dir' => $this->download_dir
];
}
public function __destruct() {
if (is_resource($this->conn)) {
ftp_close($this->conn);
}
}
}
// 使用示例
try {
$downloader = new BatchFTPDownloader(
"ftp.example.com",
"username",
"password",
"./downloads/"
);
// 批量下载指定文件
$files_to_download = [
"public_html/index.html",
"public_html/styles.css",
"public_html/script.js",
"public_html/images/logo.png",
"public_html/documents/report.pdf"
];
$results = $downloader->downloadFiles($files_to_download, "backup_");
// 生成报告
echo "\n下载报告:\n";
$report = $downloader->generateReport();
foreach ($report as $key => $value) {
echo "{$key}: {$value}\n";
}
// 下载整个目录
echo "\n下载整个目录:\n";
$downloader->downloadDirectory("public_html/logs", true);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
?>
示例5:Web界面文件下载器
<?php
// Web界面文件下载器
session_start();
// FTP配置
$ftp_config = [
'server' => 'ftp.example.com',
'username' => 'webuser',
'password' => 'password',
'root_dir' => '/public_html/files/'
];
// 处理下载请求
if (isset($_GET['download']) && !empty($_GET['file'])) {
$remote_file = basename($_GET['file']);
$full_remote_path = $ftp_config['root_dir'] . $remote_file;
// 验证文件扩展名(安全考虑)
$allowed_extensions = ['pdf', 'txt', 'jpg', 'png', 'zip', 'doc', 'xls'];
$extension = strtolower(pathinfo($remote_file, PATHINFO_EXTENSION));
if (!in_array($extension, $allowed_extensions)) {
die("不允许下载此类型的文件");
}
$conn = ftp_connect($ftp_config['server']);
if ($conn && ftp_login($conn, $ftp_config['username'], $ftp_config['password'])) {
ftp_pasv($conn, true);
// 检查文件是否存在
$file_size = ftp_size($conn, $full_remote_path);
if ($file_size != -1) {
// 设置HTTP头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $remote_file . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $file_size);
// 创建临时文件
$temp_file = tempnam(sys_get_temp_dir(), 'ftp_download_');
// 下载文件到临时文件
if (ftp_get($conn, $temp_file, $full_remote_path, FTP_BINARY)) {
// 输出文件内容
readfile($temp_file);
unlink($temp_file);
exit;
}
unlink($temp_file);
}
ftp_close($conn);
}
// 如果下载失败
header('HTTP/1.1 404 Not Found');
echo "文件下载失败";
exit;
}
// 获取文件列表
$file_list = [];
$conn = ftp_connect($ftp_config['server']);
if ($conn && ftp_login($conn, $ftp_config['username'], $ftp_config['password'])) {
ftp_pasv($conn, true);
$files = ftp_nlist($conn, $ftp_config['root_dir']);
if ($files !== false) {
foreach ($files as $file) {
$filename = basename($file);
if ($filename != '.' && $filename != '..') {
$file_size = ftp_size($conn, $ftp_config['root_dir'] . $filename);
if ($file_size != -1) {
$file_icon = getFileIcon($filename);
$file_list[] = [
'name' => $filename,
'size' => $file_size,
'formatted_size' => formatFileSize($file_size),
'icon' => $file_icon,
'modified' => ftp_mdtm($conn, $ftp_config['root_dir'] . $filename)
];
}
}
}
}
ftp_close($conn);
}
// 辅助函数
function getFileIcon($filename) {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$icons = [
'pdf' => 'fa-file-pdf',
'txt' => 'fa-file-alt',
'jpg' => 'fa-file-image',
'jpeg' => 'fa-file-image',
'png' => 'fa-file-image',
'gif' => 'fa-file-image',
'zip' => 'fa-file-archive',
'rar' => 'fa-file-archive',
'doc' => 'fa-file-word',
'docx' => 'fa-file-word',
'xls' => 'fa-file-excel',
'xlsx' => 'fa-file-excel',
'php' => 'fa-file-code',
'html' => 'fa-file-code',
'css' => 'fa-file-code',
'js' => 'fa-file-code'
];
return isset($icons[$extension]) ? $icons[$extension] : 'fa-file';
}
function formatFileSize($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';
} else {
return $bytes . ' 字节';
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FTP文件下载器</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.file-item {
transition: all 0.2s;
border-radius: 5px;
}
.file-item:hover {
background-color: #f8f9fa;
transform: translateY(-2px);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.file-icon {
font-size: 1.5em;
color: #6c757d;
}
.file-icon-pdf { color: #e74c3c; }
.file-icon-image { color: #3498db; }
.file-icon-archive { color: #f39c12; }
.file-icon-document { color: #2ecc71; }
.file-icon-code { color: #9b59b6; }
.download-btn {
transition: all 0.2s;
}
.download-btn:hover {
transform: scale(1.05);
}
.file-size {
font-size: 0.9em;
color: #6c757d;
}
</style>
</head>
<body>
<div class="container mt-4">
<div class="row mb-4">
<div class="col">
<h1><i class="fas fa-cloud-download-alt me-2"></i>FTP文件下载器</h1>
<p class="text-muted">从FTP服务器下载文件</p>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">服务器信息</h5>
</div>
<div class="card-body">
<ul class="list-unstyled mb-0">
<li><i class="fas fa-server me-2"></i> <?php echo $ftp_config['server']; ?></li>
<li><i class="fas fa-folder me-2"></i> <?php echo $ftp_config['root_dir']; ?></li>
<li><i class="fas fa-file me-2"></i> <?php echo count($file_list); ?> 个文件</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="mb-0">文件类型说明</h5>
</div>
<div class="card-body">
<ul class="list-unstyled mb-0">
<li><i class="fas fa-file-pdf text-danger me-2"></i> PDF文档</li>
<li><i class="fas fa-file-image text-primary me-2"></i> 图片文件</li>
<li><i class="fas fa-file-archive text-warning me-2"></i> 压缩文件</li>
<li><i class="fas fa-file-word text-success me-2"></i> 文档文件</li>
<li><i class="fas fa-file-code text-purple me-2"></i> 代码文件</li>
</ul>
</div>
</div>
</div>
<div class="col-md-9">
<div class="card">
<div class="card-header">
<h5 class="mb-0">文件列表</h5>
</div>
<div class="card-body">
<?php if (empty($file_list)): ?>
<div class="text-center py-5">
<i class="fas fa-folder-open fa-3x text-muted mb-3"></i>
<p class="text-muted">没有找到文件</p>
</div>
<?php else: ?>
<div class="row row-cols-1 row-cols-md-2 g-4">
<?php foreach ($file_list as $file): ?>
<div class="col">
<div class="file-item border rounded p-3 h-100">
<div class="d-flex align-items-center mb-2">
<i class="fas <?php echo $file['icon']; ?> file-icon me-3"></i>
<div class="flex-grow-1">
<h6 class="mb-0" title="<?php echo htmlspecialchars($file['name']); ?>">
<?php
if (strlen($file['name']) > 30) {
echo htmlspecialchars(substr($file['name'], 0, 30)) . '...';
} else {
echo htmlspecialchars($file['name']);
}
?>
</h6>
<div class="file-size">
<?php echo $file['formatted_size']; ?>
<?php if ($file['modified']): ?>
<span class="ms-2">
<i class="far fa-clock me-1"></i>
<?php echo date('Y-m-d', $file['modified']); ?>
</span>
<?php endif; ?>
</div>
</div>
</div>
<div class="text-end">
<a href="?download=1&file=<?php echo urlencode($file['name']); ?>"
class="btn btn-sm btn-primary download-btn">
<i class="fas fa-download me-1"></i>下载
</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 为下载按钮添加点击效果
document.querySelectorAll('.download-btn').forEach(function(btn) {
btn.addEventListener('click', function(e) {
var filename = this.href.split('file=')[1];
if (filename) {
filename = decodeURIComponent(filename);
// 显示下载提示
var toast = document.createElement('div');
toast.className = 'position-fixed bottom-0 end-0 p-3';
toast.style.zIndex = '11';
toast.innerHTML = `
<div id="downloadToast" class="toast show" role="alert">
<div class="toast-header">
<i class="fas fa-download text-primary me-2"></i>
<strong class="me-auto">正在下载</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
</div>
<div class="toast-body">
正在下载文件: <strong>${filename}</strong>
</div>
</div>
`;
document.body.appendChild(toast);
// 5秒后自动移除提示
setTimeout(function() {
if (toast.parentNode) {
document.body.removeChild(toast);
}
}, 5000);
}
});
});
});
</script>
</body>
</html>
传输模式说明
FTP传输模式:
| 模式常量 |
值 |
描述 |
适用文件类型 |
FTP_ASCII |
1 |
ASCII文本模式,自动转换行结束符 |
.txt, .html, .php, .css, .js, .json, .xml等文本文件 |
FTP_BINARY |
2 |
二进制模式,原样传输字节 |
.jpg, .png, .zip, .exe, .pdf, .mp4等二进制文件 |
注意:使用错误的模式可能导致文件损坏。文本文件使用ASCII模式可以确保行结束符在不同系统间正确转换。
与ftp_fget()的区别
| 对比项 |
ftp_get() |
ftp_fget() |
| 输出目标 |
直接保存到指定路径的文件 |
写入到已打开的文件指针 |
| 灵活性 |
简单直接,适合常规下载 |
更灵活,可以自定义文件处理 |
| 内存使用 |
自动管理,适合大多数场景 |
可以流式处理,适合大文件 |
| 断点续传 |
支持(通过$resumepos参数) |
支持(通过$resumepos参数) |
| 适用场景 |
简单文件下载、常规下载任务 |
需要自定义处理、流式处理大文件 |
常见问题
- 权限问题:FTP用户没有读取远程文件的权限
- 路径错误:远程文件路径不正确或文件不存在
- 本地磁盘问题:本地磁盘空间不足或没有写入权限
- 网络问题:连接中断或超时
- 防火墙:FTP被动模式被防火墙阻止
- 文件锁定:远程文件被其他进程锁定
- 模式错误:使用了错误的传输模式导致文件损坏
<?php
// 根据文件扩展名自动选择传输模式
function getTransferModeForDownload($filename) {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$text_extensions = [
'txt', 'html', 'htm', 'php', 'css', 'js',
'json', 'xml', 'csv', 'ini', 'conf', 'log',
'md', 'markdown', 'yml', 'yaml', 'sql',
'sh', 'bat', 'py', 'java', 'cpp', 'h'
];
$binary_extensions = [
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'svg',
'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
'zip', 'rar', '7z', 'tar', 'gz', 'bz2',
'exe', 'dll', 'so', 'dmg', 'iso',
'mp3', 'mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv'
];
if (in_array($extension, $text_extensions)) {
return FTP_ASCII;
} elseif (in_array($extension, $binary_extensions)) {
return FTP_BINARY;
} else {
// 默认使用二进制模式
return FTP_BINARY;
}
}
// 使用示例
$files = [
'document.pdf' => 'PDF文档',
'script.php' => 'PHP脚本',
'image.jpg' => 'JPEG图片',
'data.txt' => '文本文件',
'archive.zip' => '压缩文件'
];
foreach ($files as $filename => $description) {
$mode = getTransferModeForDownload($filename);
echo "{$description} ({$filename}): " .
($mode == FTP_ASCII ? "ASCII模式" : "二进制模式") . "\n";
}
?>
- 设置超时时间:使用
ftp_set_option($conn, FTP_TIMEOUT_SEC, 300)增加超时时间
- 使用被动模式:
ftp_pasv($conn, true)避免防火墙问题
- 实现断点续传:使用
$resumepos参数支持恢复下载
- 分块下载:将大文件分成多个部分下载
- 错误重试:实现自动重试机制,特别是网络不稳定的环境
- 进度监控:显示下载进度,提高用户体验
- 验证完整性:下载完成后比较远程和本地文件的大小和哈希值
相关函数
| 函数 |
描述 |
ftp_fget() |
从FTP服务器下载文件到已打开的文件指针 |
ftp_put() |
上传文件到FTP服务器 |
ftp_nb_get() |
非阻塞方式从FTP服务器下载文件 |
ftp_size() |
获取远程文件大小 |
ftp_mdtm() |
获取远程文件的最后修改时间 |
file_get_contents() |
将整个文件读入一个字符串 |
最佳实践:
- 下载前检查远程文件是否存在和可访问
- 根据文件类型选择合适的传输模式
- 下载完成后验证文件完整性
- 为下载的文件设置合理的本地路径和权限
- 实现错误处理和日志记录机制
- 对于公开下载,考虑添加下载次数统计和流量控制
- 大文件下载考虑使用非阻塞函数(
ftp_nb_get())