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

PHP 檔案上傳核心代碼解析:$_FILES 超全局變數與 move_uploaded_file 實戰

     編輯:PHP 相關知識

最小可行上傳代碼

<?php
// upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!empty($_FILES['myfile']['name'])) {
        if ($_FILES['myfile']['error'] > 0) {
            die("錯誤狀態:{$_FILES['myfile']['error']}");
        }

        $dest = "uploads/" . basename($_FILES['myfile']['name']);
        if (move_uploaded_file($_FILES['myfile']['tmp_name'], $dest)) {
            echo "<script>alert('上傳成功!');</script>";
        } else {
            die("移動檔案失敗,請檢查目錄權限");
        }
    } else {
        echo "<script>alert('請選擇檔案!');</script>";
    }
}
?>
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="myfile">
    <input type="submit" value="上傳">
</form>

關鍵知識點解析

1. $_FILES 結構說明

$_FILES['myfile'] 中的 myfile 對應 HTML ``:

鍵名說明範例
name用戶端原始檔名photo.jpg
typeMIME 類型 (瀏覽器提供,不可信)image/jpeg
tmp_name伺服器暫存路徑/tmp/phpA1B2C3
error錯誤代碼 (0=成功)0, 1, 2, 3, 4, 6, 7, 8
size檔案大小 (位元組)102400

2. 錯誤代碼對照

UPLOAD_ERR_OK           = 0  // 成功
UPLOAD_ERR_INI_SIZE     = 1  // 超過 php.ini upload_max_filesize
UPLOAD_ERR_FORM_SIZE    = 2  // 超過表單 MAX_FILE_SIZE
UPLOAD_ERR_PARTIAL      = 3  // 部分上傳
UPLOAD_ERR_NO_FILE      = 4  // 未選擇檔案
UPLOAD_ERR_NO_TMP_DIR   = 6  // 缺少暫存目錄
UPLOAD_ERR_CANT_WRITE   = 7  // 寫入失敗
UPLOAD_ERR_EXTENSION    = 8  // PHP 擴展阻擋

3. move_uploaded_file() 必須用於驗證

僅有 move_uploaded_file() 或 is_uploaded_file() 能確認檔案真經由 HTTP POST 上傳,防止路徑遍歷攻擊。

進階:安全上傳類別

<?php
class SafeUploader {
    private $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    private $maxSize = 2 * 1024 * 1024; // 2MB
    private $uploadDir = 'uploads/';
    private $allowedExt = ['jpg', 'jpeg', 'png', 'gif', 'webp'];

    public function upload($fieldName) {
        if (!isset($_FILES[$fieldName]) || $_FILES[$fieldName]['error'] !== UPLOAD_ERR_OK) {
            throw new Exception('上傳失敗或無檔案: ' . ($_FILES[$fieldName]['error'] ?? '未知'));
        }

        $file = $_FILES[$fieldName];

        // 1. MIME 類型檢查 (參考用,不可完全信任)
        if (!in_array($file['type'], $this->allowedTypes)) {
            throw new Exception('不允許的檔案類型: ' . $file['type']);
        }

        // 2. 副檔名檢查
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, $this->allowedExt)) {
            throw new Exception('不允許的副檔名: ' . $ext);
        }

        // 3. 檔案大小
        if ($file['size'] > $this->maxSize) {
            throw new Exception('檔案過大,最大 ' . ($this->maxSize/1024/1024) . 'MB');
        }

        // 4. 內容偵測 (防偽裝檔案)
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $realMime = finfo_file($finfo, $file['tmp_name']);
        finfo_close($finfo);
        if (!in_array($realMime, $this->allowedTypes)) {
            throw new Exception('檔案內容類型不符: ' . $realMime);
        }

        // 5. 產生安全檔名
        $safeName = bin2hex(random_bytes(16)) . '.' . $ext;
        $destPath = $this->uploadDir . $safeName;

        // 6. 確保目錄存在
        if (!is_dir($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }

        // 7. 移動檔案
        if (!move_uploaded_file($file['tmp_name'], $destPath)) {
            throw new Exception('檔案移動失敗');
        }

        return [
            'original_name' => $file['name'],
            'saved_name'    => $safeName,
            'path'          => $destPath,
            'size'          => $file['size'],
            'mime'          => $realMime
        ];
    }
}

// 使用
try {
    $uploader = new SafeUploader();
    $result = $uploader->upload('myfile');
    echo "上傳成功: " . json_encode($result);
} catch (Exception $e) {
    echo "錯誤: " . $e->getMessage();
}
?>

php.ini 關鍵設定

file_uploads = On
upload_max_filesize = 10M
post_max_size = 12M          ; 必須 > upload_max_filesize
max_file_uploads = 20        ; 單次最多檔案數
upload_tmp_dir = /tmp        ; 暫存目錄需可寫
max_execution_time = 60      ; 大檔案需調大
memory_limit = 128M

常見坑位

  1. enctype 遺漏:`
    ` 必須有 `enctype="multipart/form-data"`
  2. 權限問題:`uploads/` 目錄需 Web 使用者寫入權限 (Linux: `chown www-data:www-data uploads`)
  3. 暫存目錄滿/無權限:檢查 `upload_tmp_dir` 與 `open_basedir`
  4. 檔名中文亂碼:儲存時統一用隨機名,原始檔名存資料庫
  5. 大檔案上傳中斷:調整 `post_max_size`、`max_execution_time`、Nginx `client_max_body_size`

掌握 $_FILES 結構、錯誤代碼、move_uploaded_file 驗證機制,並實施 MIME/副檔名/內容/大小多層檢查,是構建安全檔案上傳功能的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved