PHP项目如何实现文件下载功能?

wen PHP项目 12

本文目录导读:

PHP项目如何实现文件下载功能?

  1. 基本文件下载(直接链接)
  2. 通过URL参数下载(常见方式)
  3. 大文件下载(分块传输)
  4. 断点续传下载
  5. 下载不同类型的文件
  6. HTML下载按钮示例
  7. 重要注意事项

在PHP中实现文件下载功能有多种方法,下面我介绍几种常见的实现方式。

基本文件下载(直接链接)

最简单的下载方式,文件放在服务器可访问的目录:

// 文件在服务器上的路径
$file = 'path/to/your/file.pdf';
$filename = 'download.pdf'; // 下载时显示的文件名
if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    readfile($file);
    exit;
}

通过URL参数下载(常见方式)

<?php
// download.php?id=123
$fileId = $_GET['id'];
// 从数据库获取文件信息
$fileInfo = getFileInfoFromDatabase($fileId); // 自定义函数
$file = 'uploads/' . $fileInfo['stored_name'];
$filename = $fileInfo['original_name'];
if (file_exists($file)) {
    // 设置下载头
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    // 清空输出缓冲
    ob_clean();
    flush();
    // 输出文件内容
    readfile($file);
    exit;
} else {
    echo '文件不存在';
}
?>

大文件下载(分块传输)

对于大文件,使用分块传输避免内存溢出:

function downloadFile($file, $filename = null) {
    if (!file_exists($file)) {
        return false;
    }
    if (!$filename) {
        $filename = basename($file);
    }
    // 获取文件大小
    $fileSize = filesize($file);
    // 设置下载头
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . $fileSize);
    // 清空输出缓冲
    ob_clean();
    flush();
    // 打开文件
    $handle = fopen($file, 'rb');
    // 分块读取并输出
    $chunkSize = 1024 * 1024; // 1MB
    while (!feof($handle)) {
        echo fread($handle, $chunkSize);
        ob_flush();
        flush();
    }
    fclose($handle);
    exit;
}
// 使用示例
downloadFile('large_file.mp4', '我的视频.mp4');

断点续传下载

function resumeDownload($file, $filename = null) {
    if (!file_exists($file)) {
        return false;
    }
    if (!$filename) {
        $filename = basename($file);
    }
    $fileSize = filesize($file);
    $start = 0;
    $end = $fileSize - 1;
    // 处理断点续传
    if (isset($_SERVER['HTTP_RANGE'])) {
        $range = $_SERVER['HTTP_RANGE'];
        $range = str_replace('bytes=', '', $range);
        $range = explode('-', $range);
        if (isset($range[0]) && $range[0] !== '') {
            $start = intval($range[0]);
        }
        if (isset($range[1]) && $range[1] !== '') {
            $end = intval($range[1]);
        }
        header('HTTP/1.1 206 Partial Content');
        header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
    } else {
        header('HTTP/1.1 200 OK');
    }
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Content-Length: ' . ($end - $start + 1));
    $handle = fopen($file, 'rb');
    fseek($handle, $start);
    $chunkSize = 1024 * 1024;
    $remaining = ($end - $start + 1);
    while ($remaining > 0) {
        $readSize = min($chunkSize, $remaining);
        echo fread($handle, $readSize);
        $remaining -= $readSize;
        ob_flush();
        flush();
    }
    fclose($handle);
    exit;
}

下载不同类型的文件

根据文件类型设置正确的MIME类型:

function getMimeType($file) {
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_file($finfo, $file);
    finfo_close($finfo);
    return $mimeType;
}
function downloadWithCorrectType($file, $filename = null) {
    if (!file_exists($file)) {
        return false;
    }
    if (!$filename) {
        $filename = basename($file);
    }
    $mimeType = getMimeType($file);
    header('Content-Description: File Transfer');
    header('Content-Type: ' . $mimeType);
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}

HTML下载按钮示例

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>文件下载</title>
</head>
<body>
    <h2>文件下载</h2>
    <ul>
        <li>
            <a href="download.php?id=1">下载文档1.pdf</a>
        </li>
        <li>
            <a href="download.php?id=2">下载图片2.jpg</a>
        </li>
        <li>
            <form action="download.php" method="post">
                <input type="hidden" name="file_id" value="3">
                <button type="submit">下载视频3.mp4</button>
            </form>
        </li>
    </ul>
</body>
</html>

重要注意事项

  1. 安全性:始终验证用户是否有权限下载文件
  2. 路径安全:防止目录遍历攻击
  3. 文件验证:确保文件存在且可读
  4. 大文件处理:使用分块传输处理大文件
  5. 错误处理:添加适当的错误处理机制
// 安全验证示例
function validateDownload($fileId, $userId) {
    // 验证用户权限
    if (!hasPermission($userId, 'download')) {
        return false;
    }
    // 验证文件属于该用户
    $file = getFileById($fileId);
    if ($file['user_id'] !== $userId) {
        return false;
    }
    return true;
}
// 防止目录遍历
function sanitizeFilename($filename) {
    $filename = basename($filename);
    $filename = str_replace(['../', '..\\', './', '.\\'], '', $filename);
    $filename = preg_replace('/[^\w\-_\. ]/', '', $filename);
    return $filename;
}

选择合适的下载方式取决于你的具体需求:小文件用简单方法,大文件用分块传输,需要续传功能用断点续传实现。

抱歉,评论功能暂时关闭!