首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 文件上傳與下載完整實現指南

     編輯:PHP 綜合知識

文件上傳/下載是 Web 開發基礎功能,但細節決定安全與體驗。本文梳理從前端表單到後端處理的完整流程,指出原文代碼的安全隱患,並給出加固版實現。

上傳端關鍵點

  • 表單必須:enctype="multipart/form-data"、method="POST"、<input type="file">。
  • PHP 配置:file_uploads=On、upload_max_filesize、post_max_size、max_file_uploads、upload_tmp_dir 權限。
  • 安全檢查:MIME 類型白名單、擴展名白名單、文件頭魔數驗證、隨機重命名存儲、移出 Web 目錄或禁用執行權限。

加固版上傳代碼

<?php
// upload.php
$allowedMime = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$allowedExt  = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$maxSize     = 5 * 1024 * 1024; // 5MB
$uploadDir   = __DIR__ . '/storage/uploads/'; // 非 Web 可訪問目錄

if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die('上傳錯誤碼:' . $file['error']);
    }
    if ($file['size'] > $maxSize) die('文件過大');

    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!in_array($ext, $allowedExt)) die('擴展名不允許');

    // 檢測真實 MIME(需 fileinfo 擴展)
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime  = $finfo->file($file['tmp_name']);
    if (!in_array($mime, $allowedMime)) die('文件類型不允許');

    // 隨機文件名防覆蓋與遍歷
    $newName = bin2hex(random_bytes(16)) . '.' . $ext;
    $dest    = $uploadDir . $newName;

    if (!move_uploaded_file($file['tmp_name'], $dest)) {
        die('移動文件失敗');
    }
    echo json_encode(['success' => true, 'filename' => $newName, 'original' => $file['name']]);
    exit;
}
?>
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" accept="image/*,.pdf" required>
    <button type="submit">上傳</button>
</form>

下載端關鍵點

  • 原文直接用 $_GET['FileName'] 拼接路徑,存在目錄遍歷漏洞(../../etc/passwd)。
  • 需建立「邏輯文件名 → 物理路徑」映射表(資料庫),下載時查表獲取真實路徑。
  • 大文件使用 X-Sendfile (Apache) / X-Accel-Redirect (Nginx) 交給 Web 服務器傳輸,PHP 僅做權限校驗,避免阻塞進程。

安全下載代碼

<?php
// download.php?id=uuid
$id = $_GET['id'] ?? '';
// 1. 查資料庫獲取物理路徑、原始文件名、MIME
$stmt = $pdo->prepare('SELECT path, original_name, mime FROM files WHERE id=? AND deleted=0');
$stmt->execute([$id]);
$file = $stmt->fetch();
if (!$file || !is_file($file['path'])) { http_response_code(404); exit('文件不存在'); }

// 2. 權限檢查(略)

// 3. 交給 Nginx 發送
header('X-Accel-Redirect: /protected-files/' . basename($file['path']));
header('Content-Type: ' . $file['mime']);
header('Content-Disposition: attachment; filename="' . rawurlencode($file['original_name']) . '"');
header('Content-Length: ' . filesize($file['path']));
exit;
?>

Nginx 配置:

location /protected-files/ {
    internal; # 僅允許內部重定向
    alias /var/www/storage/uploads/;
}

進階建議

  • 上傳後異步掃毒、生成縮圖、轉碼(FFmpeg/ImageMagick)。
  • 對象存儲(S3/OSS/COS)+ 簽名直傳,減輕應用服務器壓力。
  • 實現斷點續傳:前端 uppy/resumable.js + 後端分片合併。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved