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

Yii2 整合 Plupload 實現多圖上傳、自動縮圖、即時預覽

     編輯:PHP 相關知識

Yii2 整合 Plupload 實現多圖上傳、自動縮圖、即時預覽

Plupload 是強大的前端上傳組件,支援 HTML5/Flash/Silverlight 多運行時、分塊上傳、客戶端縮圖。本文展示 Yii2 完整整合方案:Widget 封裝、Asset 管理、JS 交互、後端模型驗證與縮圖處理。

架構概覽

  • Widget:common\widgets\Plupload - 渲染上傳區域、註冊 JS 初始化
  • Asset Bundles:PluploadAsset(核心庫)、UploadAsset(業務 JS)
  • Controller:FileController::actionUpload() - 接收上傳、回傳 JSON
  • Model:common\models\ImageUpload - 驗證、儲存、縮圖核心邏輯

1. 視圖調用 Widget

// views/article/_form.php
use common\widgets\Plupload;

echo Plupload::widget([
    'model' => $model,
    'attribute' => 'cover_img',
    'url' => '/file/upload',  // 上傳處理端點
]);

2. Widget 類別

namespace common\widgets;

use backend\assets\UploadAsset;
use yii\base\Exception;
use yii\base\Widget;
use yii\helpers\Html;

class Plupload extends Widget {
    public $model;
    public $attribute;
    public $url;

    public function init() {
        parent::init();
        if (!$this->url) throw new Exception('url 參數必填');
        if (!$this->model) throw new Exception('model 屬性必填');
        if (!$this->attribute) throw new Exception('attribute 屬性必填');
    }

    public function run() {
        $model = $this->model;
        $attr = $this->attribute;
        $value = $model->$attr ?: '/images/noimage.gif';

        $html = '
'; $html .= Html::activeLabel($model, $attr); $html .= Html::activeHiddenInput($model, $attr, ['id' => 'hidden_input', 'value' => $value]); $html .= '
'; $html .= '
'; UploadAsset::register($this->view); $csrf = Yii::$app->request->csrfToken; $this->view->registerJs( "initCoverImageUploader('pickfiles','container','2mb','{$this->url}','{$csrf}');" ); return $html; } }

3. Asset Bundles

// PluploadAsset - 核心庫
namespace backend\assets;
use yii\web\AssetBundle;

class PluploadAsset extends AssetBundle {
    public $sourcePath = '@vendor/moxiecode/plupload';
    public $js = ['js/plupload.full.min.js'];
    public $depends = ['yii\web\JqueryAsset'];
}

// UploadAsset - 業務邏輯 JS
class UploadAsset extends AssetBundle {
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $js = ['js/upload.js'];
    public $depends = ['backend\assets\PluploadAsset'];
}

4. 前端 JS 交互 (js/upload.js)

function initCoverImageUploader(btnId, containerId, maxSize, url, csrfToken) {
    var uploader = new plupload.Uploader({
        runtimes: 'html5,flash,silverlight,html4',
        browse_button: btnId,
        container: containerId,
        url: url,
        flash_swf_url: '@vendor/moxiecode/plupload/js/Moxie.swf',
        silverlight_xap_url: '@vendor/moxiecode/plupload/js/Moxie.xap',
        filters: {
            max_file_size: maxSize,
            mime_types: [
                {title: "Image files", extensions: "jpg,gif,png"},
                {title: "Zip files", extensions: "zip"}
            ]
        },
        multipart_params: { '_csrf': csrfToken },
        init: {
            FilesAdded: function(up, files) { up.start(); },
            UploadProgress: function(up, file) {
                $('#' + containerId + ' p').text('已上傳: ' + file.percent + '%');
            },
            FileUploaded: function(up, file, result) {
                var res = JSON.parse(result.response);
                if (res.code == 0) {
                    $('#' + btnId).html('');
                    $('#hidden_input').val(res.path);
                }
            },
            Error: function(up, err) {
                console.error("Plupload Error #" + err.code + ": " + err.message);
            }
        }
    });
    uploader.init();
}

5. 控制器

namespace backend\controllers;

use yii\web\Controller;
use yii\web\Response;
use common\models\ImageUpload;

class FileController extends Controller {
    public function actionUpload() {
        Yii::$app->response->format = Response::FORMAT_JSON;
        $model = new ImageUpload();
        $model->fileInputName = 'file'; // 對應 Plupload 預設檔案欄位名
        if ($model->save()) {
            return ['code' => 0, 'message' => $model->getMessage(), 'path' => $model->getUrlPath()];
        }
        return ['code' => 1, 'message' => $model->getMessage()];
    }
}

6. 核心模型:驗證、儲存、縮圖

namespace common\models;

use yii\base\Exception;
use yii\helpers\FileHelper;
use yii\web\UploadedFile;
use yii\base\Object;

class ImageUpload extends Object {
    public $fileInputName = 'file';
    public $savePath = '@backend/web/uploads/images';
    public $allowExt = ['jpg','png','jpeg','gif','bmp'];
    public $maxFileSize = 2097152; // 2MB
    public $allowType = ['image/jpeg','image/png','image/gif','image/bmp','image/pjpeg','image/x-png'];

    private $_uploadFile;
    private $filePath;
    private $urlPath;
    private $res = false;
    private $message = '';

    public function init() {
        if (!$this->fileInputName) throw new Exception('fileInputName 必填');
        $this->savePath = Yii::getAlias(rtrim($this->savePath, '/'));
        if (!file_exists($this->savePath)) {
            FileHelper::createDirectory($this->savePath);
        }
        $this->_uploadFile = UploadedFile::getInstanceByName($this->fileInputName);
        if (!$this->_uploadFile) { $this->message = '未找到上傳檔案'; return; }
        if ($this->_uploadFile->error) { $this->message = '上傳失敗'; return; }
        if (!in_array($this->extension, $this->allowExt) || !in_array($this->type, $this->allowType)) {
            $this->message = '檔案類型不允許'; return;
        }
        if ($this->_uploadFile->size > $this->maxFileSize) { $this->message = '檔案過大'; return; }

        $dateDir = date('Y-m');
        $absDir = $this->savePath . '/' . $dateDir;
        if (!file_exists($absDir)) FileHelper::createDirectory($absDir);

        $rand = substr(Yii::$app->security->generateRandomString(), -4, 4);
        $this->filePath = "$absDir/{$this->baseName}--$rand.$this->extension";
        $this->urlPath = "/uploads/images/$dateDir/{$this->baseName}--$rand.$this->extension";
    }

    public function save() {
        if ($this->_uploadFile->saveAs($this->filePath)) {
            self::CreateThumbnail($this->filePath); // 縮圖
            $this->res = true;
            $this->message = $this->_uploadFile->baseName . '.' . $this->_uploadFile->extension . ' 上傳成功';
        } else {
            $this->message = '上傳失敗';
        }
        return $this->res;
    }

    // --- 縮圖核心:等比縮放、支援 jpg/png/gif、統一輸出 jpg ---
    public static function CreateThumbnail($srcFile, $toFile = "", $toW = 100, $toH = 100) {
        if ($toFile == "") $toFile = $srcFile;
        $data = getimagesize($srcFile);
        if (!$data) return false;

        switch ($data[2]) {
            case 1: $im = imagecreatefromgif($srcFile); break;
            case 2: $im = imagecreatefromjpeg($srcFile); break;
            case 3: $im = imagecreatefrompng($srcFile); break;
            default: return false;
        }

        $srcW = imagesx($im); $srcH = imagesy($im);
        $toWH = $toW / $toH; $srcWH = $srcW / $srcH;

        if ($toWH <= $srcWH) {
            $ftoW = $toW; $ftoH = (int)($ftoW * ($srcH / $srcW));
        } else {
            $ftoH = $toH; $ftoW = (int)($ftoH * ($srcW / $srcH));
        }

        $ni = function_exists("imagecreatetruecolor")
            ? imagecreatetruecolor($ftoW, $ftoH)
            : imagecreate($ftoW, $ftoH);

        if (function_exists("imagecopyresampled")) {
            imagecopyresampled($ni, $im, 0, 0, 0, 0, $ftoW, $ftoH, $srcW, $srcH);
        } else {
            imagecopyresized($ni, $im, 0, 0, 0, 0, $ftoW, $ftoH, $srcW, $srcH);
        }

        switch ($data[2]) {
            case 1: imagegif($ni, $toFile); break;
            case 2: imagejpeg($ni, $toFile, 90); break;
            case 3: imagepng($ni, $toFile); break;
        }
        imagedestroy($ni); imagedestroy($im);
        return $toFile;
    }

    // Getter 方法...
    public function getMessage() { return $this->message; }
    public function getUrlPath() { return $this->urlPath; }
    public function getBaseName() { return $this->_uploadFile ? $this->_uploadFile->baseName : null; }
    public function getExtension() { return $this->_uploadFile ? $this->_uploadFile->extension : null; }
    public function getType() { return $this->_uploadFile ? $this->_uploadFile->type : null; }
}

關鍵優勢

  1. 關注點分離:Widget 處理視圖、Asset 管理資源、Controller 薄封裝、Model 核心業務。
  2. 安全驗證:副檔名 + MIME 類型雙重檢查、大小限制、隨機命名防覆蓋、CSRF 保護。
  3. 高效縮圖:imagecopyresampled 高品質重採樣、自動計算等比尺寸、統一品質 90%。
  4. 即時預覽:上傳成功立即回顯縮圖,隱藏欄位同步更新路徑供表單提交。

擴展建議

  • 支援分塊上傳大檔案(Plupload chunk_size 參數)
  • 整合雲存儲(阿里云 OSS、AWS S3、七牛雲)
  • 添加圖片水印、格式轉換(WebP)、EXIF 方向自動修正
  • 前端添加拖放上傳、上傳隊列管理、失敗重試
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved