首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

Zend Framework 多檔案上傳實作:插件架構、驗證過濾、縮圖生成完整範例

     編輯:PHP 綜合知識

前言:為什麼封裝為 Controller Plugin?

Zend Framework 1.x 推薦將可複用的横切關注點 (如檔案上傳、認證、日誌) 封裝為 Zend_Controller_Plugin_Abstract 子類,掛載於 Front Controller,所有 Action 自動共享。本文示範:建立 Custom_Controller_Plugin_Upload 支援多檔、類型/大小限制、圖片縮圖,並在 Action 中一行呼叫完成上傳。

一、目錄結構與自動載入

library/
  Custom/
    Controller/
      Plugin/
        Upload.php   <-- 核心上傳類
application/
  controllers/
    IndexController.php
  views/
    scripts/
      message/
        upload.phtml

確保 library 在 include_path,並已註冊 Zend_Loader_Autoloader (或手動 require_once)。

二、Upload Plugin 核心代碼

class Custom_Controller_Plugin_Upload extends Zend_Controller_Plugin_Abstract {
    private $uploadDir;      // 存放路徑
    private $maxFiles;       // 最大檔案數
    private $maxSize;        // 單檔最大 bytes
    private $allowedExt;     // 允許副檔名陣列
    private $imageMaxW;      // 圖片最大寬度
    private $imageMaxH;      // 圖片最大高度
    public $fileArray = [];  // 成功上傳檔名陣列
    public $lastFileName;    // 最後一個檔名
    public $error = '';      // 錯誤訊息

    public function __construct($dir, $formName, $maxFiles, $maxSize, array $allowedExt, $imgW=0, $imgH=0) {
        $this->uploadDir = rtrim($dir, '/') . '/';
        $this->maxFiles = (int)$maxFiles;
        $this->maxSize = (int)$maxSize;
        $this->allowedExt = array_map('strtolower', $allowedExt);
        $this->imageMaxW = (int)$imgW;
        $this->imageMaxH = (int)$imgH;
        $this->formName = $formName;
        $this->processUpload();
    }

    private function processUpload() {
        if (!isset($_FILES[$this->formName])) { $this->error = '無上傳資料'; return; }
        $files = $this->rearrayFiles($_FILES[$this->formName]);
        if (count($files) > $this->maxFiles) { $this->error = "超過最大檔案數 {$this->maxFiles}"; return; }

        foreach ($files as $file) {
            if ($file['error'] !== UPLOAD_ERR_OK) { $this->error = $this->getUploadError($file['error']); return; }
            if ($file['size'] > $this->maxSize) { $this->error = "檔案 {$file['name']} 超過大小限制"; return; }
            $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
            if (!in_array($ext, $this->allowedExt)) { $this->error = "副檔名 .$ext 不被允許"; return; }

            $newName = $this->generateUniqueName($ext);
            $dest = $this->uploadDir . $newName;
            if (!move_uploaded_file($file['tmp_name'], $dest)) { $this->error = "移動檔案失敗"; return; }
            chmod($dest, 0644);

            // 圖片縮圖
            if ($this->imageMaxW && $this->imageMaxH && $this->isImage($ext)) {
                $this->createThumbnail($dest, $this->imageMaxW, $this->imageMaxH);
            }
            $this->fileArray[] = $newName;
            $this->lastFileName = $newName;
        }
    }

    private function rearrayFiles($files) { /* 將 $_FILES 多檔結構轉為陣列 */ }
    private function generateUniqueName($ext) { return date('YmdHis').'_'.uniqid().".$ext"; }
    private function isImage($ext) { return in_array($ext, ['jpg','jpeg','png','gif','bmp']); }
    private function createThumbnail($src, $maxW, $maxH) { /* GD 建立縮圖並覆蓋原檔 */ }
    private function getUploadError($code) { /* 對應 UPLOAD_ERR_* 常數 */ }
}

三、Controller Action 呼叫

class IndexController extends Zend_Controller_Action {
    public function uploadAction() {
        $this->view->baseUrl = $this->_request->getBaseUrl();
        if ($this->getRequest()->isPost()) {
            Zend_Loader::loadClass('Custom_Controller_Plugin_Upload');
            $upload = new Custom_Controller_Plugin_Upload(
                ROOT_DIR . '/public/upload/',  // 存放目錄
                'uploadfile',                  // 
                10,                            // 最多 10 檔
                2048000,                       // 2MB
                ['gif','png','jpg','bmp','jpeg'], // 允許類型
                600, 600                       // 縮圖最大 600x600
            );
            if ($upload->error) {
                $this->view->error = $upload->error;
            } else {
                $this->view->success = '上傳成功!檔案:' . implode(', ', $upload->fileArray);
            }
        }
        // 渲染 upload.phtml
    }
}

四、View 表單 (upload.phtml)

success)): ?>
    
success; ?>
error)): ?>
error; ?>

五、關鍵細節與擴充建議

  • 多檔 $_FILES 結構重組:PHP 上傳多檔時 $_FILES['field']['name'] 為陣列,需重組為單檔資訊陣列。
  • MIME 偽裝防護:僅檢查副檔名不足夠,建議結合 finfo_file() 檢測真實 MIME 類型。
  • 檔名衝突:使用 uniqid() + 時間戳 + 原始副檔名,或雜湊值命名。
  • 權限與安全:上傳目錄**不可執行 PHP** (Nginx/Apache 設定 location ~ \.php$ { deny all; }),檔案下載時用 readfile() 而非直接連結。
  • 大檔分片上傳:前端用 Plupload/WebUploader 分片,後端合併,Plugin 需擴充支援分片合併邏輯。
  • 雲端儲存:上傳成功後同步推至 S3/OSS/Qiniu,資料庫僅存儲雲端 URL。

結語

將檔案上傳封裝為 Controller Plugin,實現「Controller 一行代碼、View 純表單、Plugin 含驗證/縮圖/安全」的關注點分離。此架構易於單元測試、跨專案複用、功能擴充 (如加入病毒掃描、水印、轉碼)。現代專案若使用 ZF2/3 (Laminas) 或 Laravel,同樣可參考此思想建立 Service/Action/Listener 分層。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved