框架內建上傳類優勢
- 零依賴,開箱即用
- 支援多檔、多驅動 (本地、FTP、雲存儲)
- 內建驗證、子目錄哈希、命名規則等企業級特性
- 遵循 ThinkPHP 規範,與框架無縫整合
基本用法
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function upload() {
// 1. 實例化
$upload = new \Think\Upload();
// 2. 配置屬性
$upload->maxSize = 3 * 1024 * 1024; // 3MB
$upload->exts = ['jpg', 'gif', 'png', 'jpeg'];
$upload->rootPath = './Uploads/'; // 根目錄 (相對入口檔案)
$upload->savePath = ''; // 子目錄 (空則按日期自動建立)
// 3. 執行上傳
$info = $upload->upload();
// 4. 結果處理
if (!$info) {
$this->error($upload->getError());
} else {
// $info 結構: ['field_name' => ['name','savename','savepath','ext','size','type',...]]
foreach ($info as $file) {
echo "檔案: {$file['name']} → 儲存: {$file['savepath']}{$file['savename']}<br>";
}
$this->success('上傳成功!');
}
}
}
?>
核心配置屬性完整表
| 屬性 | 型別 | 預設值 | 說明 |
| maxSize | int | 0 (不限制) | 最大檔案大小 (位元組) |
| exts | array | [] | 允許副檔名 (不含點) |
| mimes | array | [] | 允許 MIME 類型 |
| rootPath | string | ./Uploads/ | 儲存根目錄 |
| savePath | string | '' | 子目錄,支援日期格式 Y/m/d |
| autoSub | bool | true | 是否自動建立子目錄 |
| subName | string/array | ['date','Ymd'] | 子目錄命名規則 |
| saveName | string/array | ['uniqid',''] | 檔案命名規則 |
| saveExt | string | '' | 強制副檔名 |
| replace | bool | false | 同名檔案是否覆蓋 |
| hash | bool | true | 是否生成 hash 目錄 |
| callback | bool | false | 是否啟用回調檢查 |
| driver | string | Local | 儲存驅動 (Local/Ftp/Qiniu...) |
| driverConfig | array | [] | 驅動配置參數 |
進階配置範例
1. 自訂命名:時間戳 + 隨機數
$upload->saveName = ['time', 'YmdHis'] . '_' . mt_rand(1000, 9999);
// 產生: 20240115143025_8765.jpg
2. 日期分層目錄結構
$upload->autoSub = true;
$upload->subName = ['date', 'Y/m/d'];
// 產生: ./Uploads/2024/01/15/
3. 多檔案上傳 (表單 name="files[]")
// 控制器同樣使用 $upload->upload()
// $info 將包含多個檔案資訊
if ($info) {
foreach ($info as $field => $file) {
// $field 為表單欄位名,多檔時通常為 0,1,2...
echo "{$file['savename']} ({$file['size']} bytes)<br>";
}
}
4. 回調函數驗證 (圖片尺寸、內容檢查)
$upload->callback = true;
// 需在同控制器定義 callback 方法
public function callback($file) {
// $file 為單檔資訊陣列
$imgInfo = getimagesize($file['tmp_name']);
if ($imgInfo[0] > 2000 || $imgInfo[1] > 2000) {
return '圖片尺寸不得超過 2000x2000';
}
// 檢查是否為真實圖片 (防偽裝)
if (!in_array($imgInfo['mime'], ['image/jpeg','image/png','image/gif'])) {
return '非法圖片格式';
}
return true; // 通過
}
5. 雲存儲驅動 (七牛雲範例)
$upload->driver = 'Qiniu';
$upload->driverConfig = [
'accessKey' => 'your_ak',
'secretKey' => 'your_sk',
'domain' => 'https://cdn.yourdomain.com',
'bucket' => 'your_bucket',
'timeout' => 30,
];
前端表單關鍵點
<form action="<?php echo U('Index/upload'); ?>" method="post" enctype="multipart/form-data">
<!-- 單檔 -->
<input type="file" name="photo" accept="image/*">
<!-- 多檔 -->
<input type="file" name="files[]" multiple accept="image/*">
<!-- 限制大小 (瀏覽器端提示,伺服器仍需驗證) -->
<input type="hidden" name="MAX_FILE_SIZE" value="3145728">
<button type="submit">上傳</button>
</form>
錯誤代碼對照表
| 常數 | 代碼 | 訊息 |
| UPLOAD_ERR_OK | 0 | 上傳成功 |
| UPLOAD_ERR_INI_SIZE | 1 | 超過 php.ini upload_max_filesize |
| UPLOAD_ERR_FORM_SIZE | 2 | 超過 HTML MAX_FILE_SIZE |
| UPLOAD_ERR_PARTIAL | 3 | 部分上傳 |
| UPLOAD_ERR_NO_FILE | 4 | 無檔案上傳 |
| UPLOAD_ERR_NO_TMP_DIR | 6 | 缺少暫存目錄 |
| UPLOAD_ERR_CANT_WRITE | 7 | 寫入失敗 |
| UPLOAD_ERR_EXTENSION | 8 | 擴展阻擋 |
常見問題與解決
| 問題 | 原因 | 解決 |
| 上傳大檔失敗 | php.ini 限制 | 調大 upload_max_filesize, post_max_size, max_execution_time |
| 中文檔名亂碼 | 編碼不一致 | 設定 saveName 強制重命名、或 mb_convert_encoding |
| 權限錯誤 | 目錄不可寫 | chmod 755 Uploads、檢查 PHP-FPM 用戶權限 |
| nginx 413 錯誤 | client_max_body_size | nginx.conf 設定 client_max_body_size 20M; |
最佳實踐封裝:Service 層
<?php
// Home/Service/UploadService.class.php
namespace Home\Service;
use Think\Upload;
class UploadService {
private $upload;
public function __construct(array $config = []) {
$this->upload = new Upload($config);
}
public function image(string $field, array $options = []): array {
$default = [
'maxSize' => 5 * 1024 * 1024,
'exts' => ['jpg','jpeg','png','gif','webp'],
'savePath' => 'images/',
'subName' => ['date', 'Y/m'],
];
$this->upload->maxSize = $default['maxSize'];
$this->upload->exts = $default['exts'];
$this->upload->savePath = $default['savePath'];
$this->upload->subName = $default['subName'];
return $this->upload($field);
}
public function document(string $field): array {
$this->upload->exts = ['pdf','doc','docx','xls','xlsx','txt'];
$this->upload->savePath = 'documents/';
return $this->upload($field);
}
private function upload(string $field): array {
$info = $this->upload->upload();
if (!$info) {
throw new \Exception($this->upload->getError());
}
return isset($info[$field]) ? $info[$field] : array_values($info)[0];
}
}
// 控制器使用
$service = new \Home\Service\UploadService();
try {
$file = $service->image('avatar');
// $file: ['name','savename','savepath','size','type',...]
$user->avatar = $file['savepath'] . $file['savename'];
} catch (\Exception $e) {
$this->error($e->getMessage());
}
?>
總結
ThinkPHP 3.2 的 \Think\Upload 類功能強大、配置靈活,掌握其核心屬性 (maxSize, exts, rootPath, savePath, saveName, subName, driver) 即可應對絕大多數上傳場景。建議在 Service 層封裝業務邏輯,控制器保持輕量。