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

PHP 文件上傳核心原理與安全實戰:從 $_FILES 到 move_uploaded_file

     編輯:PHP 相關知識

上傳流程核心原理

  1. 瀏覽器以 multipart/form-data 編碼發送 POST 請求
  2. Web 服務器(Nginx/Apache)接收請求體,暫存到臨時目錄
  3. PHP 啟動,將臨時文件信息填充 $_FILES 超全局數組
  4. 腳本執行驗證邏輯,通過 move_uploaded_file() 移動到永久存儲
  5. 請求結束,PHP 自動清理未被移動的臨時文件

表單必要屬性

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="MAX_FILE_SIZE" value="2000000">
    <input type="file" name="avatar">
    <button type="submit">上傳</button>
</form>
  • enctype="multipart/form-data":缺一不可,否則 $_FILES 為空
  • method="post":GET 無法上傳文件
  • MAX_FILE_SIZE:客戶端提示上限(隱藏字段,不可信任,服務端必驗證)

$_FILES 結構詳解

Array
(
    [avatar] => Array
        (
            [name]     => photo.jpg        // 客戶端原始文件名(不可信)
            [type]     => image/jpeg       // 客戶端聲明 MIME(不可信)
            [tmp_name] => /tmp/phpA1B2C3   // 服務端臨時文件路徑(唯一可信來源)
            [error]    => 0                // 錯誤碼:0=成功,1-8 對應具體錯誤
            [size]     => 102400           // 文件大小(字節)
        )
)

php.ini 關鍵配置

五層安全驗證體系(由淺入深)

<?php
function secureUpload(array $file, string $destDir, array $allowTypes, int $maxSize): array
{
    // 第 1 層:錯誤碼
    if ($file['error'] !== UPLOAD_ERR_OK) {
        throw new Exception(uploadErrorMsg($file['error']));
    }

    // 第 2 層:真實上傳文件
    if (!is_uploaded_file($file['tmp_name'])) {
        throw new Exception('非法文件來源');
    }

    // 第 3 層:MIME 類型(finfo 讀取文件頭,防偽裝)
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $realType = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);
    if (!in_array($realType, $allowTypes, true)) {
        throw new Exception("類型不允許: $realType");
    }

    // 第 4 層:圖片二次驗證(getimagesize 解析文件結構,防掛馬)
    if (str_starts_with($realType, 'image/') && $realType !== 'image/gif') {
        if (!@getimagesize($file['tmp_name'])) {
            throw new Exception('無效圖片文件');
        }
    }

    // 第 5 層:大小限制
    if ($file['size'] > $maxSize) {
        throw new Exception('文件過大');
    }

    // 生成安全文件名
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    $safeName = bin2hex(random_bytes(16)) . '.' . $ext;
    $destPath = rtrim($destDir, '/') . '/' . $safeName;

    // 移動文件(原子操作,跨分區自動複製+刪除)
    if (!move_uploaded_file($file['tmp_name'], $destPath)) {
        throw new Exception('存儲失敗');
    }

    return [
        'original' => $file['name'],
        'saved'    => $safeName,
        'path'     => $destPath,
        'size'     => $file['size'],
        'type'     => $realType,
    ];
}

function uploadErrorMsg(int $code): string
{
    return match ($code) {
        UPLOAD_ERR_INI_SIZE   => '超過 php.ini upload_max_filesize',
        UPLOAD_ERR_FORM_SIZE  => '超過表單 MAX_FILE_SIZE',
        UPLOAD_ERR_PARTIAL    => '文件部分上傳',
        UPLOAD_ERR_NO_FILE    => '無文件上傳',
        UPLOAD_ERR_NO_TMP_DIR => '臨時目錄不存在',
        UPLOAD_ERR_CANT_WRITE => '寫入失敗',
        UPLOAD_ERR_EXTENSION  => '擴展阻止上傳',
        default => '未知錯誤',
    };
}

常用輔助函數速查

指令默認建議生產值說明
file_uploadsOnOn總開關
upload_max_filesize2M10M-50M單文件上限
post_max_size8M> upload_max_filesizePOST 總體上限(含所有表單字段)
upload_tmp_dir系統臨時目錄/var/tmp/php-upload臨時文件存放,需寫權限
max_file_uploads2050單請求最大文件數
函數用途返回值
is_uploaded_file判斷是否 HTTP POST 上傳bool
move_uploaded_file移動上傳文件(安全檢查)bool
file_exists文件/目錄是否存在bool
is_writable目錄是否可寫bool
getimagesize驗證圖片並獲取尺寸array|false
finfo_file獲取真實 MIME 類型string
iconv字符編碼轉換string

進階主題

  • 大文件分片上傳:前端切片 → 並發上傳 → 後端合併 → 校驗 MD5
  • 異步處理管道:上傳完成即返回 → 隊列任務執行壓縮、水印、轉碼、掃毒
  • 雲存儲直傳:後端簽名 Policy → 前端直傳 OSS/S3 → 回調業務服務器
  • 防重複存儲:計算 SHA256 → 查詢數據庫 → 存在則返回現有路徑
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved