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

PHP 多文件上傳完整實戰:從表單構建到安全存儲與錯誤處理

     編輯:PHP 相關知識

前端表單關鍵點

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="MAX_FILE_SIZE" value="2000000">
    <input type="file" name="files[]" multiple>
    <input type="file" name="files[]" multiple>
    <input type="file" name="files[]" multiple>
    <button type="submit">上傳</button>
</form>

核心要素:

  • enctype="multipart/form-data":必須,否則 $_FILES 為空
  • name="files[]":方括號讓 PHP 自動組裝為多維數組
  • multiple:HTML5 屬性,允許單個 input 選多文件
  • MAX_FILE_SIZE:客戶端提示上限(可繞過,服務端必驗證)

$_FILES 多維結構解析

Array
(
    [files] => Array
        (
            [name] => Array
                (
                    [0] => photo1.jpg
                    [1] => photo2.png
                )
            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/png
                )
            [tmp_name] => Array
                (
                    [0] => /tmp/phpA1B2C3
                    [1] => /tmp/phpD4E5F6
                )
            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )
            [size] => Array
                (
                    [0] => 102400
                    [1] => 204800
                )
        )
)

重組為單文件結構數組(便於復用單文件邏輯)

function restructureFiles(array $files): array
{
    $count = count($files['name']);
    $restructured = [];
    for ($i = 0; $i < $count; $i++) {
        $restructured[] = [
            'name'     => $files['name'][$i],
            'type'     => $files['type'][$i],
            'tmp_name' => $files['tmp_name'][$i],
            'error'    => $files['error'][$i],
            'size'     => $files['size'][$i],
        ];
    }
    return $restructured;
}

$files = restructureFiles($_FILES['files']);

完整上傳處理邏輯(含安全防護)

<?php
$allowTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$maxSize = 2 * 1024 * 1024; // 2MB
$uploadDir = __DIR__ . '/uploads/';

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

$files = restructureFiles($_FILES['files']);
$results = [];

foreach ($files as $index => $file) {
    // 1. 錯誤碼檢查
    if ($file['error'] !== UPLOAD_ERR_OK) {
        $results[$index] = ['success' => false, 'msg' => uploadErrorMessage($file['error'])];
        continue;
    }

    // 2. 是否真實上傳文件
    if (!is_uploaded_file($file['tmp_name'])) {
        $results[$index] = ['success' => false, 'msg' => '非法上傳來源'];
        continue;
    }

    // 3. MIME 類型驗證(防偽裝)
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $realType = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);
    if (!in_array($realType, $allowTypes, true)) {
        $results[$index] = ['success' => false, 'msg' => '文件類型不允許'];
        continue;
    }

    // 4. 圖片二次驗證(防掛馬)
    if ($realType !== 'image/gif' && !@getimagesize($file['tmp_name'])) {
        $results[$index] = ['success' => false, 'msg' => '非有效圖片文件'];
        continue;
    }

    // 5. 大小限制
    if ($file['size'] > $maxSize) {
        $results[$index] = ['success' => false, 'msg' => '文件超過大小限制'];
        continue;
    }

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

    // 7. 移動文件
    if (move_uploaded_file($file['tmp_name'], $destPath)) {
        $results[$index] = [
            'success' => true,
            'original_name' => $file['name'],
            'saved_name' => $safeName,
            'path' => $destPath,
            'size' => $file['size'],
            'type' => $realType
        ];
    } else {
        $results[$index] = ['success' => false, 'msg' => '文件保存失敗'];
    }
}

// 輸出 JSON 結果
header('Content-Type: application/json');
echo json_encode(['files' => $results], JSON_UNESCAPED_UNICODE);

function uploadErrorMessage(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  => 'PHP 擴展阻斷了上傳',
        default => '未知上傳錯誤',
    };
}

進階擴展建議

  • 分片上傳大文件:前端切片,後端合併,突破 PHP post_max_size / upload_max_filesize 限制
  • 異步隊列處理:上傳即返回,圖片壓縮、水印、OCR、病毒掃描進隊列異步執行
  • 雲存儲直傳:簽名直傳 OSS/S3,減輕應用服務器帶寬壓力
  • 防重複上傳:計算文件 SHA256,去重存儲
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved