功能特性
- 支援多文件同時上傳
- 文件類型白名單驗證(圖片、文檔等)
- 文件大小限制
- 自動生成唯一文件名(隨機字符+時間戳)
- 按年月自動建立上傳目錄
- 詳細的上傳結果表格輸出
核心類代碼
<?php
class MultiFileUpload {
const UPLOAD_PATH = '../filehandle/uploadfile/';
const MAX_SIZE = 81200000; // 80MB
private $allowedTypes = [
'text/plain', 'image/gif', 'image/jpeg', 'image/png',
'text/html', 'application/msword', 'application/pdf'
];
private $uploadDir;
private $totalSize = 0;
private $results = [];
public function __construct(array $files) {
$this->uploadDir = $this->getUploadDir();
$this->processFiles($files);
}
private function getUploadDir(): string {
$base = rtrim(self::UPLOAD_PATH, '/');
$dir = $base . '/' . date('Ym');
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
return $dir . '/';
}
private function processFiles(array $files): void {
foreach ($files as $file) {
if ($file['error'] !== UPLOAD_ERR_OK) {
$this->addResult($file['name'], $file['type'], $file['size'], $this->getErrorMessage($file['error']));
continue;
}
if (!in_array($file['type'], $this->allowedTypes)) {
$this->addResult($file['name'], $file['type'], $file['size'], '文件類型不允許上傳');
continue;
}
if ($file['size'] > self::MAX_SIZE) {
$this->addResult($file['name'], $file['type'], $file['size'], '文件超過大小限制');
continue;
}
$newName = $this->generateFileName($file['name']);
$dest = $this->uploadDir . $newName;
if (move_uploaded_file($file['tmp_name'], $dest)) {
$this->totalSize += $file['size'];
$this->addResult($file['name'], $file['type'], $file['size'], '上傳成功', $newName);
} else {
$this->addResult($file['name'], $file['type'], $file['size'], '文件移動失敗');
}
}
}
private function generateFileName(string $originalName): string {
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$rand = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 10);
return 'TTR_' . time() . $rand . '.' . $ext;
}
private function getErrorMessage(int $code): string {
switch ($code) {
case UPLOAD_ERR_INI_SIZE: return '文件超過 php.ini 限制';
case UPLOAD_ERR_FORM_SIZE: return '文件超過表單 MAX_FILE_SIZE 限制';
case UPLOAD_ERR_PARTIAL: return '文件僅部分上傳';
case UPLOAD_ERR_NO_FILE: return '未選擇文件';
default: return '未知錯誤';
}
}
private function addResult(string $name, string $type, int $size, string $msg, string $savedName = ''): void {
$this->results[] = [
'original' => $name,
'type' => $type,
'size_kb' => round($size / 1024, 1),
'message' => $msg,
'saved_as' => $savedName
];
}
public function renderResults(): void {
echo '<table border="1" cellpadding="5" cellspacing="0" width="90%" align="center">';
echo '<tr><th>原文件名</th><th>類型</th><th>大小(KB)</th><th>結果</th><th>保存名稱</th></tr>';
foreach ($this->results as $r) {
echo '<tr>';
echo '<td>' . htmlspecialchars($r['original']) . '</td>';
echo '<td>' . htmlspecialchars($r['type']) . '</td>';
echo '<td align="center">' . $r['size_kb'] . '</td>';
echo '<td align="center">' . htmlspecialchars($r['message']) . '</td>';
echo '<td align="center">' . htmlspecialchars($r['saved_as']) . '</td>';
echo '</tr>';
}
echo '</table>';
echo '<p>總計大小:' . round($this->totalSize / 1024 / 1024, 2) . ' MB</p>';
}
}
?>
前端表單示例
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple><br>
<input type="submit" value="上傳">
</form>
處理頁面 upload.php
<?php
require_once 'MultiFileUpload.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_FILES['files'])) {
// 整理 $_FILES 結構以便遍歷
$files = [];
$count = count($_FILES['files']['name']);
for ($i = 0; $i < $count; $i++) {
$files[] = [
'name' => $_FILES['files']['name'][$i],
'type' => $_FILES['files']['type'][$i],
'tmp_name' => $_FILES['files']['tmp_name'][$i],
'error' => $_FILES['files']['error'][$i],
'size' => $_FILES['files']['size'][$i]
];
}
$uploader = new MultiFileUpload($files);
$uploader->renderResults();
}
?>
優化建議
- 生產環境請將允許類型、大小限制、上傳路徑提取到配置文件。
- 增加圖片二次驗證(如
getimagesize())防止偽裝文件。
- 上傳目錄應置於 Web 根目錄外,或通過 .htaccess 禁止腳本執行。
- 考慮使用 PDO 記錄上傳日誌到數據庫。