文件上傳是 Web 開發中的高頻需求。將上傳邏輯封裝為可復用類,不僅能統一錯誤處理,還能輕鬆擴展大小限制、類型白名單、存儲路徑策略等功能。本文提供一個精簡的 UploadHandler 類,演示核心流程與最佳實踐。
<?php
class UploadHandler
{
/** @var int 允許的最大文件大小(位元組) */
private $maxSize;
/** @var string 目標目錄(以 / 結尾) */
private $targetDir;
/** @var array 允許的 MIME 類型 */
private $allowedTypes = [
'image/jpeg' => '.jpg',
'image/pjpeg' => '.jpg',
'image/gif' => '.gif',
'image/png' => '.png',
];
public function __construct(int $maxSize = 500000, string $targetDir = 'uploads/')
{
$this->maxSize = $maxSize;
$this->targetDir = rtrim($targetDir, '/') . '/';
if (!is_dir($this->targetDir)) {
mkdir($this->targetDir, 0755, true);
}
}
/**
* 執行上傳
* @param array $file $_FILES 中的單個文件項
* @param string $newName 自定義文件名(不含副檔名),預設 'date' 表示用 YmdHis
* @return string|false 成功返回相對路徑,失敗返回 false
*/
public function upload(array $file, string $newName = 'date')
{
// 1. 基礎驗證
if ($file['error'] !== UPLOAD_ERR_OK) {
error_log("上傳錯誤代碼:{$file['error']}");
return false;
}
if ($file['size'] > $this->maxSize) {
error_log("文件過大:{$file['size']} > {$this->maxSize}");
return false;
}
$ext = $this->allowedTypes[$file['type']] ?? null;
if (!$ext) {
error_log("不支援的類型:{$file['type']}");
return false;
}
// 2. 確定文件名
if ($newName === 'date') {
$newName = date('YmdHis');
}
$destPath = $this->targetDir . $newName . $ext;
// 3. 移動文件
if (move_uploaded_file($file['tmp_name'], $destPath)) {
return $newName . $ext; // 返回相對路徑
}
error_log("移動文件失敗:{$file['tmp_name']} -> {$destPath}");
return false;
}
}
<?php
require_once 'UploadHandler.php';
$uploader = new UploadHandler(500000, 'data/avatars/');
$result = $uploader->upload($_FILES['avatar'], 'user_123');
if ($result) {
echo "上傳成功,文件:{$result}";
// 此處可將 $result 存入資料庫
} else {
echo "上傳失敗,請檢查日誌";
}
$_FILES 陣列,批次調用 upload()。move_uploaded_file 替換為 S3/OSS SDK 上傳邏輯,介面保持不變。error_log,方便上層統一捕獲並返回 JSON 錯誤碼。將上傳邏輯集中管理,既能減少控制器代碼,又能在需求變更時單點修改,是中大型項目的推薦做法。