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。

返回值

  • 成功时返回 true
  • 失败时返回 false

示例

示例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参数)
适用场景 简单文件下载、常规下载任务 需要自定义处理、流式处理大文件

常见问题

  1. 权限问题:FTP用户没有读取远程文件的权限
  2. 路径错误:远程文件路径不正确或文件不存在
  3. 本地磁盘问题:本地磁盘空间不足或没有写入权限
  4. 网络问题:连接中断或超时
  5. 防火墙:FTP被动模式被防火墙阻止
  6. 文件锁定:远程文件被其他进程锁定
  7. 模式错误:使用了错误的传输模式导致文件损坏

<?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()