類設計目標
- 支持多種圖片格式(JPG、PNG、GIF、BMP 等)
- 文件大小限制、MIME 類型雙重驗證
- 自動創建存儲目錄、生成唯一文件名
- 可選生成縮略圖預覽
- 統一異常處理與友好提示
核心類代碼
<?php
class ImageUploader {
// 配置屬性
private $uploadDir = './uploads/';
private $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
private $maxSize = 2 * 1024 * 1024; // 2MB
private $thumbRatio = 0.2; // 縮略圖比例
private $createThumb = true;
// 運行時屬性
private $fileName;
private $tmpName;
private $fileSize;
private $fileType;
private $imageInfo;
public function __construct(array $config = []) {
foreach ($config as $key => $val) {
if (property_exists($this, $key)) $this->$key = $val;
}
}
public function upload(array $file): array {
$this->init($file);
$this->validate();
$this->save();
return $this->getResult();
}
private function init(array $file) {
$this->tmpName = $file['tmp_name'];
$this->fileName = $file['name'];
$this->fileSize = $file['size'];
$this->fileType = $file['type'];
$this->imageInfo = getimagesize($this->tmpName);
if (!$this->imageInfo) throw new Exception('非法圖片文件');
}
private function validate() {
if (!is_dir($this->uploadDir)) mkdir($this->uploadDir, 0755, true);
if ($this->fileSize > $this->maxSize)
throw new Exception('文件大小超過限制 (' . ($this->maxSize/1024/1024) . 'MB)');
if (!in_array($this->fileType, $this->allowedTypes))
throw new Exception('不支持的文件類型:' . $this->fileType);
}
private function save() {
$ext = strtolower(pathinfo($this->fileName, PATHINFO_EXTENSION));
$newName = date('YmdHis') . '_' . uniqid() . '.' . $ext;
$destPath = $this->uploadDir . $newName;
if (!move_uploaded_file($this->tmpName, $destPath))
throw new Exception('文件移動失敗,請檢查目錄權限');
$this->fileName = $newName;
$this->destPath = $destPath;
}
private function getResult(): array {
$result = [
'success' => true,
'filename' => $this->fileName,
'path' => $this->destPath,
'url' => $this->destPath, // 相對 Web 根目錄的 URL
'size' => $this->fileSize,
'width' => $this->imageInfo[0],
'height' => $this->imageInfo[1],
];
if ($this->createThumb) {
$result['thumb'] = $this->createThumbnail();
}
return $result;
}
private function createThumbnail(): string {
// 簡易縮略:直接縮放尺寸屬性,實際項目建議用 GD/Imagick 生成物理縮略圖
$w = $this->imageInfo[0] * $this->thumbRatio;
$h = $this->imageInfo[1] * $this->thumbRatio;
return '<img src="' . $this->destPath . '" width="' . $w . '" height="' . $h . '">';
}
}
前端表單
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="photo" accept="image/*" required>
<button type="submit">上傳圖片</button>
</form>
處理頁面
<?php
require 'ImageUploader.php';
header('Content-Type: application/json; charset=utf-8');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit(json_encode(['success' => false, 'msg' => '請求方式錯誤']));
}
try {
$uploader = new ImageUploader([
'uploadDir' => __DIR__ . '/public/uploads/',
'maxSize' => 5 * 1024 * 1024,
]);
$result = $uploader->upload($_FILES['photo']);
echo json_encode($result);
} catch (Exception $e) {
http_response_code(400);
echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
}
安全加固清單
- 禁用腳本執行權限: 上傳目錄配置 Nginx/Apache 禁止 PHP 執行。
- 二次驗證文件內容: 使用
finfo_file() 檢測真實 MIME,防止偽造 Content-Type。
- 文件名去敏: 不保留用戶原始文件名,防止路徑遍歷攻擊。
- 病毒掃描: 生產環境接入 ClamAV 等掃描引擎。
- 限制並發與總量: 防止磁盤被寫滿。