在 Web 開發中,檔案上傳是極為常見的功能需求。單檔上傳相對簡單,但當面對多檔案同時上傳、副檔名驗證、錯誤處理、亂碼檔名生成與目錄結構管理等綜合場景時,若無完善的封裝,程式碼極易變得混亂難維護。本文將深入解析一個功能完整的 Files_Tool 靜態類別,展示如何以物件導向方式優雅地實作 PHP 多檔案上傳核心邏輯。
Files_Tool 採用靜態方法設計,無需實例化即可直接呼叫,適合作為工具類別在專案中複用。其核心屬性包含三大支柱:
$allowExt):涵蓋圖片、文件、壓縮包等常見格式,預設支援 .jpg、.png、.pdf、.zip 等 15 種格式,可依專案需求彈性擴充。$error):將 PHP 內建上傳錯誤代碼(0-8)與自訂業務錯誤(9)映射為人類可讀的中文訊息,包含檔案過大、部分上傳、臨時目錄遺失、寫入失敗等 10 種情境。$wrong、$path):分別記錄失敗檔案的錯誤代碼與成功檔案的相對路徑,支援批次處理時的精準回報。upload($name) 方法首先檢查 $_FILES[$name] 是否存在,若缺少表單欄位則回傳錯誤代碼 8。隨後透過 array_shift($_FILES) 將三維陣列扁平化為二維結構,並呼叫 get_Ext() 統一將副檔名轉為小寫,消除大小寫不一致導致的驗證漏洞。
核心迴圈對每個檔案執行四層檢查:
$files['error'][$i] != 0 直接記錄對應錯誤代碼並跳過。in_array($files['name'][$i], self::$allowExt) 攔截惡意檔案類型。time_Dir() 依照 Y/m/d 格式建立多層級目錄(如 Data/images/2024/05/20/),自動遞迴建立且權限設為 0777。rand_Name() 以 str_shuffle 打亂英數字元取前 6 碼,極大降低衝突機率。使用 move_uploaded_file() 將臨時檔案移至目標路徑,失敗則記錄錯誤代碼 9。成功檔案的路徑經 strtr($path, array(ROOT => '')) 轉為相對路徑存入 $path 陣列,便於資料庫存儲與前端回顯。
time_Dir)採用 date('Y/m/d/') 格式,將檔案按天分散儲存,避免單一目錄檔案過多導致檔案系統效能下降。mkdir($dir, 0777, true) 的遞迴參數確保多層目錄一次建立成功。
rand_Name)字元池包含 0-9 及 a-z 共 36 個字元,str_shuffle 打亂後取前 6 碼,理論組合數達 36^6 ≈ 21 億,足以應對高併發場景。若專案需求更高唯一性,可改用 uniqid() 或 UUID。
errors)將儲存的數字代碼轉換為「第 N 個檔案:具體錯誤原因」的完整句子,直接可用於前端提示或日誌記錄,免去呼叫端再次映射的麻煩。
ROOT 路徑與 $allowExt 白名單移至配置檔,避免硬編碼。MAX_FILE_SIZE、PHP 端調整 upload_max_filesize 與 post_max_size,形成雙重保障。finfo_file() 檢測真實 MIME 類型,防止偽裝副檔名的惡意檔案繞過驗證。<?php
define('ROOT', 'D:/Program Files/www/test/');
class Files_Tool {
protected static $allowExt = array('.jpg','.jpeg','.png','.gif','.bmp','.svg','.chm','.pdf','.zip','.rar','.tar','.gz','.bzip2','.ppt','.doc');
public static $wrong = array();
public static $path = array();
protected static $error = array(
0 => '文件上傳成功',
1 => '超過 php.ini upload_max_filesize 限制',
2 => '超過表單 MAX_FILE_SIZE 限制',
3 => '檔案只有部分被上傳',
4 => '沒有檔案被上傳',
5 => '不允許的副檔名',
6 => '找不到臨時資料夾',
7 => '檔案寫入失敗',
8 => '未接收到表單欄位 NAME',
9 => '未知錯誤'
);
public static function upload($name) {
if (!isset($_FILES[$name])) {
self::$wrong[] = 8;
return false;
}
$files = array_shift($_FILES);
$files = self::get_Ext($files);
$n = count($files['name']);
for ($i = 0; $i < $n; $i++) {
if ($files['error'][$i] != 0) {
self::$wrong[$i+1] = $files['error'][$i];
continue;
}
if (!in_array($files['name'][$i], self::$allowExt)) {
self::$wrong[$i+1] = 5;
continue;
}
$dir = self::time_Dir();
$name = self::rand_Name();
$ext = $files['name'][$i];
$path = $dir . $name . $ext;
if (!move_uploaded_file($files['tmp_name'][$i], $path)) {
self::$wrong[$i] = 9;
continue;
}
self::$path[$i+1] = strtr($path, array(ROOT => ''));
}
return self::$path;
}
protected static function get_Ext($arr) {
if (!is_array($arr) || !isset($arr['name'])) return false;
foreach ($arr['name'] as $k => $v) {
$arr['name'][$k] = strtolower(strrchr($v, '.'));
}
return $arr;
}
protected static function time_Dir() {
$dir = ROOT . 'Data/images/' . date('Y/m/d/', time());
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
return $dir;
}
protected static function rand_Name() {
$str = str_shuffle('1234567890qwertyuiopasdfghjklzxcvbnm');
return substr($str, 0, 6);
}
public static function errors() {
foreach (self::$wrong as $k => $v) {
self::$wrong[$k] = '第 ' . $k . ' 個 ' . self::$error[$k];
}
return self::$wrong;
}
}
?>
本文展示的 Files_Tool 類別展現了 PHP 多檔案上傳的最佳實踐模式:靜態工具類封裝、白名單機制、分層目錄管理、隨機命名策略、完善錯誤處理與人性化回報。開發者可直接引入專案,或依據業務需求擴充檔案類型驗證、圖片壓縮、雲端儲存對接等進階功能,構建企業級檔案上傳服務。