ThinkPHP 內建 UploadFile 與 Image 類庫,提供強大的檔案上傳、縮略圖生成、水印添加功能。本文以完整模組為例,示範從資料表設計、配置、控制器、視圖到業務邏輯的端到端實作。
CREATE TABLE `tp_image` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`image` VARCHAR(200) NOT NULL COMMENT '儲存檔名(不含路徑)',
`create_time` INT(11) NOT NULL COMMENT '上傳時間戳',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
Conf/config.php<?php
return [
'URL_MODEL' => 2, // PATHINFO 模式
'DB_TYPE' => 'mysql',
'DB_HOST' => 'localhost',
'DB_NAME' => 'thinkphp',
'DB_USER' => 'root',
'DB_PWD' => '',
'DB_PORT' => '3306',
'DB_PREFIX' => 'tp_',
'SHOW_PAGE_TRACE' => true, // 開發環境開啟 Trace
// 模板路徑替換:__UPLOAD__ → /Uploads
'TMPL_PARSE_STRING' => [
'__UPLOAD__' => __ROOT__ . '/Uploads',
],
];
Lib/Action/ImageAction.class.php<?php
class ImageAction extends Action {
/** 列表頁:顯示最新上傳圖片 */
public function index() {
$image = M('Image');
$data = $image->order('create_time DESC')->find();
$this->assign('data', $data);
$this->display();
}
/** 上傳入口:驗證檔案後轉交 _upload() */
public function upload() {
if (!empty($_FILES)) {
$this->_upload();
} else {
$this->error('未收到上傳檔案');
}
}
/** 核心上傳邏輯 */
protected function _upload() {
import('@.ORG.UploadFile');
$upload = new UploadFile();
// 基礎設定
$upload->maxSize = 3 * 1024 * 1024; // 3MB
$upload->allowExts = ['jpg', 'gif', 'png', 'jpeg'];
$upload->savePath = './Uploads/';
$upload->saveRule = 'uniqid'; // 唯一檔名
$upload->thumb = true; // 啟用縮略圖
$upload->imageClassPath = '@.ORG.Image';
$upload->thumbPrefix = 'm_,s_'; // 兩組前綴:中圖、小圖
$upload->thumbMaxWidth = '400,100';
$upload->thumbMaxHeight = '400,100';
$upload->thumbRemoveOrigin = true; // 刪除原圖
// 執行上傳
if (!$upload->upload()) {
$this->error($upload->getErrorMsg());
}
$info = $upload->getUploadFileInfo();
// $info[0] 包含:savepath, savename, name, type, size, extension...
// 給中圖 (m_) 添加水印
import('@.ORG.Image');
Image::water(
$info[0]['savepath'] . 'm_' . $info[0]['savename'],
APP_PATH . 'Tpl/Public/Images/logo.png'
);
// 寫入資料庫
$model = M('Image');
$data = [
'image' => $info[0]['savename'],
'create_time' => NOW_TIME,
];
if ($model->add($data)) {
$this->success('上傳成功!');
} else {
$this->error('資料庫寫入失敗');
}
}
}
?>
Tpl/Image/index.html<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>圖片上傳示例</title>
<style>
#img { height: 22px; border: 2px solid #000; }
#btn { height: 30px; width: 100px; }
</style>
</head>
<body>
<div class="result">
允許:gif、png、jpg。生成 2 張縮略圖(中圖帶水印),刪除原圖。
</div><br>
<notempty name="data">
<img src="__UPLOAD__/m_{$data.image}" alt="中圖(含水印)">
<img src="__UPLOAD__/s_{$data.image}" alt="小圖">
</notempty>
<form action="__URL__/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" id="img" required>
<input type="submit" value="上傳" id="btn">
</form>
</body>
</html>
thumbPrefix => 'm_,s_' 會產生:
abc123.jpg(若 thumbRemoveOrigin=false 保留)m_abc123.jpg(400x400,帶水印)s_abc123.jpg(100x100)必須在 $upload->upload() 成功後、縮略圖已生成時呼叫 Image::water(),目標檔案為 m_ 前綴的縮略圖。
savename),不儲存完整路徑__UPLOAD__ 模板變數拼接 URL,支援遷移存儲路徑(如遷移至 OSS/CDN)./Uploads/ 需給 Web 使用者寫入權限:chown www:www Uploads && chmod 755 Uploads<?php
// 1. 檔案型別二次驗證(防偽裝副檔名)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['image']['tmp_name']);
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowedMimes)) {
$this->error('檔案型別不允許');
}
// 2. 檔案內容掃描(可整合 ClamAV)
// 3. 重新命名:已使用 uniqid(),避免覆蓋與遍歷
// 4. 存儲分離:生產環境建議掛載獨立磁碟或使用 S3/OSS
?>
| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| 上傳失敗「目錄不可寫」 | 目錄權限不足或 open_basedir 限制 | 檢查 php.ini open_basedir、目錄擁有者權限 |
| 縮略圖未生成 | GD 擴展未安裝或版本過舊 | php -m | grep gd 確認;安裝 php-gd |
| 水印位置偏移 | 原圖尺寸小於水印圖 | Image 類會自動縮放水印,檢查 logo.png 尺寸 |
| 中文檔名亂碼 | 檔案系統編碼不一致 | 強制使用 uniqid()/md5() 重命名,不保留原檔名 |
這套方案在 ThinkPHP 3.2.x 驗證通過,ThinkPHP 5/6/8 同理可用,只需調整命名空間引入方式(use think\facade\Filesystem 等)。掌握核心參數與流程,即可快速構建穩健的圖片上傳服務。