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

PHP 檔案上傳下載完整實戰指南:從原理到物件導向封裝

     編輯:PHP 綜合知識

全方位掌握 PHP 檔案上傳下載核心技術與最佳實踐

檔案上傳是 Web 開發高頻需求,涉及客戶端協議、伺服器配置、安全驗證、多檔處理等多個面向。本文從 HTTP 原理出發,層層遞進至物件導向封裝與下載實作。

一、上傳核心原理與客戶端配置

基本流程

  1. 表單 method="post" enctype="multipart/form-data"
  2. <input type="file" name="myFile"> 選擇檔案
  3. 瀏覽器將檔案編碼為 multipart 格式傳送
  4. PHP 將檔案暫存至 upload_tmp_dir($_FILES['tmp_name'])
  5. 腳本驗證後,move_uploaded_file() 移至永久目錄

關鍵表單屬性

  • enctype:必須設為 multipart/form-data
  • accept:客戶端檔案類型過濾(僅 UI 提示,不可信)
  • MAX_FILE_SIZE:隱藏欄位限制大小(客戶端可繞過,不可信)

二、伺服器端驗證與安全防護

$_FILES 結構解析

Array(
    [myFile] => Array(
        [name]     => 原始檔名
        [type]     => MIME 類型(瀏覽器提供,不可信)
        [tmp_name] => 暫存路徑(關鍵)
        [error]    => 錯誤代碼(0=成功)
        [size]     => 檔案大小(位元組)
    )
)

五層防禦驗證體系

function validateUpload($fileInfo, $config) {
    // 1. 錯誤代碼檢查
    if ($fileInfo['error'] !== UPLOAD_ERR_OK) {
        throw new Exception(getUploadErrorMessage($fileInfo['error']));
    }
    
    // 2. 檔案大小限制(php.ini + 程式雙重保障)
    if ($fileInfo['size'] > $config['maxSize']) {
        throw new Exception('檔案超過大小限制');
    }
    
    // 3. 副檔名白名單(而非黑名單)
    $ext = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION));
    if (!in_array($ext, $config['allowExt'])) {
        throw new Exception('不允許的檔案類型');
    }
    
    // 4. MIME 類型二次驗證
    if (!in_array($fileInfo['type'], $config['allowMime'])) {
        throw new Exception('MIME 類型不符');
    }
    
    // 5. 真實內容驗證(防偽裝圖片)
    if ($config['checkImage'] && !@getimagesize($fileInfo['tmp_name'])) {
        throw new Exception('非真實圖片檔案');
    }
    
    // 6. 上傳方式驗證
    if (!is_uploaded_file($fileInfo['tmp_name'])) {
        throw new Exception('非 HTTP POST 上傳');
    }
}

三、檔案命名與儲存策略

// 唯一檔名:防覆蓋、防猜測、防注入
$uniName = md5(uniqid(microtime(true), true)) . '.' . $ext;
$destination = $uploadPath . '/' . $uniName;

// 目錄自動建立
if (!file_exists($uploadPath)) {
    mkdir($uploadPath, 0777, true);
}

// 移動檔案(相當於剪貼,暫存檔消失)
if (!move_uploaded_file($fileInfo['tmp_name'], $destination)) {
    throw new Exception('檔案移動失敗');
}

四、多檔案上傳處理

方式一:多個 input name 不同


foreach ($_FILES as $fileInfo) {
    uploadFile($fileInfo);  // 復用單檔邏輯
}

方式二:同名陣列 name="files[]"(推薦)


// $_FILES 結構變為三維陣列,需正規化
function normalizeFiles() {
    $files = [];
    foreach ($_FILES as $field) {
        if (is_string($field['name'])) {
            $files[] = $field;  // 單檔
        } else {
            // 多檔:重組為單檔結構陣列
            foreach ($field['name'] as $i => $name) {
                $files[] = [
                    'name'     => $name,
                    'type'     => $field['type'][$i],
                    'tmp_name' => $field['tmp_name'][$i],
                    'error'    => $field['error'][$i],
                    'size'     => $field['size'][$i],
                ];
            }
        }
    }
    return $files;
}

五、物件導向封裝類別

class FileUploader {
    protected $config = [
        'maxSize'    => 5 * 1024 * 1024,
        'allowExt'   => ['jpg', 'jpeg', 'png', 'gif'],
        'allowMime'  => ['image/jpeg', 'image/png', 'image/gif'],
        'uploadPath' => './uploads',
        'checkImage' => true,
    ];
    protected $fileInfo;
    protected $error;
    
    public function __construct($fieldName, array $customConfig = []) {
        $this->config = array_merge($this->config, $customConfig);
        $this->fileInfo = $_FILES[$fieldName];
    }
    
    public function upload() {
        $this->validate();
        $this->ensureDirectory();
        $dest = $this->generateDestination();
        if (!move_uploaded_file($this->fileInfo['tmp_name'], $dest)) {
            throw new Exception('移動失敗');
        }
        return $dest;
    }
    
    // 驗證、目錄、命名等私有方法...
}

六、檔案下載實作

// download.php
$filename = basename($_GET['file']);  // 防止目錄遍歷
$filepath = '/var/www/files/' . $filename;

if (!file_exists($filepath)) {
    header('HTTP/1.1 404 Not Found');
    exit;
}

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . filesize($filepath));
header('Cache-Control: no-cache, must-revalidate');
readfile($filepath);  // 直接輸出,不佔用 PHP 記憶體

關鍵配置檢查清單

php.ini 設定建議值說明
file_uploadsOn啟用上傳
upload_max_filesize10M單檔上限
post_max_size20MPOST 總上限 > upload_max_filesize
max_file_uploads20單次最大檔案數
upload_tmp_dir/tmp/uploads暫存目錄(需寫入權限)
max_execution_time300大檔上傳需較長時間
memory_limit128M下載用 readfile 不佔記憶體

掌握這套完整體系,從原理到封裝,讓檔案上傳下載功能安全、高效、可維護。

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