功能需求
- 支援 JPG、PNG、GIF、BMP 等常見圖片格式
- 限制檔案大小(預設 2MB)
- 上傳後自動生成縮圖預覽
- 檔案命名避免衝突(時間戳 + 隨機字串)
- 資料夾權限自動建立
前端表單 (upload.html)
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>頭像上傳</title>
<style>
body { font-family: sans-serif; max-width: 600px; margin: 2rem auto; }
.preview { max-width: 200px; max-height: 200px; border: 1px solid #ddd; margin-top: 1rem; }
.error { color: #c62828; font-weight: bold; }
.success { color: #2e7d32; font-weight: bold; }
</style>
</head>
<body>
<h1>上傳頭像</h1>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label>
選擇圖片 (JPG/PNG/GIF/BMP, < 2MB):<br>
<input type="file" name="avatar" accept="image/jpeg,image/png,image/gif,image/bmp" required>
</label><br><br>
<button type="submit">上傳並預覽</button>
</form>
<div id="result"></div>
</body>
</html>
後端處理 (upload.php)
<?php
// 設定
$allowedMimes = [
'image/jpeg', 'image/jpg', 'image/pjpeg',
'image/png', 'image/x-png',
'image/gif',
'image/bmp'
];
$maxSize = 2 * 1024 * 1024; // 2MB
$uploadDir = __DIR__ . '/uploads/avatars/';
$previewRatio = 0.5; // 縮圖比例
// 確保上傳目錄存在
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// 處理上傳
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_FILES['avatar']) || $_FILES['avatar']['error'] !== UPLOAD_ERR_OK) {
$msg = ['type' => 'error', 'text' => '檔案上傳失敗,錯誤代碼: ' . ($_FILES['avatar']['error'] ?? '未知')];
} else {
$file = $_FILES['avatar'];
// 檢查大小
if ($file['size'] > $maxSize) {
$msg = ['type' => 'error', 'text' => '檔案過大,限制 2MB'];
}
// 檢查 MIME
elseif (!in_array($file['type'], $allowedMimes, true)) {
$msg = ['type' => 'error', 'text' => '不支援的檔案類型: ' . htmlspecialchars($file['type'])];
}
// 檢查真實圖片內容 (防偽裝)
elseif (!@getimagesize($file['tmp_name'])) {
$msg = ['type' => 'error', 'text' => '檔案非有效圖片或已損毀'];
}
else {
// 產生唯一檔名
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$ext = ($ext === 'jpg') ? 'jpeg' : $ext; // 統一副檔名
$newName = 'avatar_' . date('YmdHis') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
$destPath = $uploadDir . $newName;
// 移動檔案
if (move_uploaded_file($file['tmp_name'], $destPath)) {
// 生成縮圖資訊
$imgInfo = getimagesize($destPath);
$previewW = (int)($imgInfo[0] * $previewRatio);
$previewH = (int)($imgInfo[1] * $previewRatio);
$msg = [
'type' => 'success',
'text' => '上傳成功!',
'file' => $newName,
'path' => $destPath,
'url' => 'uploads/avatars/' . $newName,
'width' => $imgInfo[0],
'height' => $imgInfo[1],
'preview_w' => $previewW,
'preview_h' => $previewH,
];
} else {
$msg = ['type' => 'error', 'text' => '檔案儲存失敗,請檢查目錄權限'];
}
}
}
// 輸出 JSON 供 AJAX 使用,或直接顯示
header('Content-Type: application/json; charset=utf-8');
echo json_encode($msg);
exit;
}
?>
AJAX 版前端增強 (upload_ajax.html)
<script>
document.querySelector('form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const resultDiv = document.getElementById('result');
resultDiv.innerHTML = '<p>上傳中...</p>';
try {
const res = await fetch('upload.php', { method: 'POST', body: formData });
const data = await res.json();
if (data.type === 'success') {
resultDiv.innerHTML = `
<p class="success">${data.text}</p>
<img src="${data.url}" class="preview" alt="預覽" width="${data.preview_w}" height="${data.preview_h}">
<p>原始尺寸: ${data.width} x ${data.height}</p>
<p>檔案名: ${data.file}</p>
`;
} else {
resultDiv.innerHTML = `<p class="error">${data.text}</p>`;
}
} catch (err) {
resultDiv.innerHTML = `<p class="error">網路錯誤: ${err.message}</p>`;
}
});
</script>
進階功能擴充
- 圖片裁切:整合 Cropper.js 讓使用者自訂裁切區域
- 多尺寸縮圖:上傳後同步生成 50x50、100x100、200x200 三種尺寸,適用不同顯示場景
- 雲端儲存:搭配 AWS S3 / Google Cloud Storage / 阿里云 OSS,將本地檔案同步上傳並回傳 CDN 網址
- 內容安全掃描:整合 ClamAV 或 Google Cloud Vision API 掃描惡意內容