* @link www.crazydb.com * * UEditor版本v1.4.3.1 * Yii 版本 2.0+ */ namespace crazydb\ueditor; use yii; use yii\imagine\Image; use yii\web\Controller; /** * Class UEditorController * 负责UEditor后台响应 * @package crazydb\ueditor */ class UEditorController extends Controller { /** * UEditor的配置 * @see http://fex-team.github.io/ueditor/#start-config * @var array */ public $config; /** * 列出文件/图片时需要忽略的文件夹 * 主要用于处理缩略图管理,兼容比如elFinder之类的程序 * @var array */ public $ignoreDir = [ '.thumbnails' ]; /** * 缩略图设置 * 默认为200*200,如果设置为空数组则不生成缩略图 * @var array */ public $thumbnail = ['height' => 200, 'width' => 200]; /** * 图片缩放设置 * 默认不缩放。 * 配置如 ['height'=>200,'width'=>200] * @var array */ public $zoom = []; /** * 水印设置 * 参考配置如下: * ['path'=>'水印图片位置','start'=>[0, 0]] * 默认位置为[0, 0],可不配置 * @var array */ public $watermark = []; /** * 默认 action * @var string */ public $defaultAction = 'index'; /** * Web根目录 * @var string */ protected $webroot; public function init() { parent::init(); //CSRF 基于 POST 验证,UEditor 无法添加自定义 POST 数据,同时由于这里不会产生安全问题,故简单粗暴地取消 CSRF 验证。 //如需 CSRF 防御,可以使用 server_param 方法,然后在这里将 Get 的 CSRF 添加到 POST 的数组中。。。 Yii::$app->request->enableCsrfValidation = false; //当客户使用低版本IE时,会使用swf上传插件,维持认证状态可以参考文档UEditor「自定义请求参数」部分。 //http://fex.baidu.com/ueditor/#server-server_param //保留UE默认的配置引入方式 if (file_exists(__DIR__ . '/config.json')) $CONFIG = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", '', file_get_contents(__DIR__ . '/config.json')), true); else $CONFIG = []; if (!is_array($this->config)) $this->config = []; if (!is_array($CONFIG)) $CONFIG = []; $default = [ 'imagePathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:8}', 'scrawlPathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:8}', 'snapscreenPathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:8}', 'catcherPathFormat' => '/upload/image/{yyyy}{mm}{dd}/{time}{rand:8}', 'videoPathFormat' => '/upload/video/{yyyy}{mm}{dd}/{time}{rand:8}', 'filePathFormat' => '/upload/file/{yyyy}{mm}{dd}/{rand:8}_{filename}', 'imageManagerListPath' => '/upload/image/', 'fileManagerListPath' => '/upload/file/', ]; $this->config = $this->config + $default + $CONFIG; $this->webroot = Yii::getAlias('@webroot'); if(!is_array($this->thumbnail)) $this->thumbnail = false; } /** * 蛋疼的统一后台入口 */ public function actionIndex() { $action = strtolower(Yii::$app->request->get('action', 'config')); $actions = [ 'uploadimage' => 'upload-image', 'uploadscrawl' => 'upload-scrawl', 'uploadvideo' => 'upload-video', 'uploadfile' => 'upload-file', 'listimage' => 'list-image', 'listfile' => 'list-file', 'catchimage' => 'catch-image', 'config' => 'config', 'listinfo' => 'list-info' ]; if (isset($actions[$action])) return $this->run($actions[$action]); else return $this->show(['state' => 'Unknown action.']); } /** * 显示配置信息 */ public function actionConfig() { return $this->show($this->config); } /** * 上传图片 */ public function actionUploadImage() { $config = [ 'pathFormat' => $this->config['imagePathFormat'], 'maxSize' => $this->config['imageMaxSize'], 'allowFiles' => $this->config['imageAllowFiles'] ]; $fieldName = $this->config['imageFieldName']; $result = $this->upload($fieldName, $config); return $this->show($result); } /** * 上传涂鸦 */ public function actionUploadScrawl() { $config = [ 'pathFormat' => $this->config['scrawlPathFormat'], 'maxSize' => $this->config['scrawlMaxSize'], 'allowFiles' => $this->config['scrawlAllowFiles'], 'oriName' => 'scrawl.png' ]; $fieldName = $this->config['scrawlFieldName']; $result = $this->upload($fieldName, $config, 'base64'); return $this->show($result); } /** * 上传视频 */ public function actionUploadVideo() { $config = [ 'pathFormat' => $this->config['videoPathFormat'], 'maxSize' => $this->config['videoMaxSize'], 'allowFiles' => $this->config['videoAllowFiles'] ]; $fieldName = $this->config['videoFieldName']; $result = $this->upload($fieldName, $config); return $this->show($result); } /** * 上传文件 */ public function actionUploadFile() { $config = [ 'pathFormat' => $this->config['filePathFormat'], 'maxSize' => $this->config['fileMaxSize'], 'allowFiles' => $this->config['fileAllowFiles'] ]; $fieldName = $this->config['fileFieldName']; $result = $this->upload($fieldName, $config); return $this->show($result); } /** * 文件列表 */ public function actionListFile() { $allowFiles = $this->config['fileManagerAllowFiles']; $listSize = $this->config['fileManagerListSize']; $path = $this->config['fileManagerListPath']; $result = $this->manage($allowFiles, $listSize, $path); return $this->show($result); } /** * 图片列表 */ public function actionListImage() { $allowFiles = $this->config['imageManagerAllowFiles']; $listSize = $this->config['imageManagerListSize']; $path = $this->config['imageManagerListPath']; $result = $this->manage($allowFiles, $listSize, $path); return $this->show($result); } /** * 获取远程图片 */ public function actionCatchImage() { /* 上传配置 */ $config = [ 'pathFormat' => $this->config['catcherPathFormat'], 'maxSize' => $this->config['catcherMaxSize'], 'allowFiles' => $this->config['catcherAllowFiles'], 'oriName' => 'remote.png' ]; $fieldName = $this->config['catcherFieldName']; /* 抓取远程图片 */ $list = []; if (isset($_POST[$fieldName])) { $source = $_POST[$fieldName]; } else { $source = $_GET[$fieldName]; } foreach ($source as $imgUrl) { $item = new Uploader($imgUrl, $config, 'remote'); $info = $item->getFileInfo(); $info['thumbnail'] = $this->imageHandle($info['url']); $list[] = [ 'state' => $info['state'], 'url' => $info['url'], 'source' => $imgUrl ]; } /* 返回抓取数据 */ return [ 'state' => count($list) ? 'SUCCESS' : 'ERROR', 'list' => $list ]; } /** * 各种上传 * @param $fieldName * @param $config * @param $base64 * @return array */ protected function upload($fieldName, $config, $base64 = 'upload') { $up = new Uploader($fieldName, $config, $base64); $info = $up->getFileInfo(); if ($this->thumbnail && $info['state'] == 'SUCCESS' && in_array($info['type'], ['.png', '.jpg', '.bmp', '.gif'])) { $info['thumbnail'] = Yii::$app->request->baseUrl . $this->imageHandle($info['url']); } $info['url'] = Yii::$app->request->baseUrl . $info['url']; $info['original'] = htmlspecialchars($info['original']); $info['width'] = $info['height'] = 500; return $info; } /** * 自动处理图片 * @param $fullName * @return mixed|string */ protected function imageHandle($fullName) { if (substr($fullName, 0, 1) != '/') $fullName = '/' . $fullName; $file = $fullName; //先处理缩略图 if ($this->thumbnail && !empty($this->thumbnail['height']) && !empty($this->thumbnail['width'])) { $file = pathinfo($file); $file = $file['dirname'] . '/' . $file['filename'] . '_thumbnail.' . $file['extension']; Image::thumbnail($this->webroot . $fullName, intval($this->thumbnail['width']), intval($this->thumbnail['height'])) ->save($this->webroot . $file); } //再处理缩放,默认不缩放 //...缩放效果非常差劲-,- if (isset($this->zoom['height']) && isset($this->zoom['width'])) { $size = $this->getSize($this->webroot . $fullName); if ($size && $size[0] > 0 && $size[1] > 0) { $ratio = min([$this->zoom['height'] / $size[0], $this->zoom['width'] / $size[1], 1]); Image::thumbnail($this->webroot . $fullName, ceil($size[0] * $ratio), ceil($size[1] * $ratio)) ->save($this->webroot . $fullName); } } //最后生成水印 if (isset($this->watermark['path']) && file_exists($this->watermark['path'])) { if (!isset($this->watermark['start'])) $this->watermark['start'] = [0, 0]; Image::watermark($file, $this->watermark['path'], $this->watermark['start']) ->save($file); } return $file; } /** * 获取图片的大小 * 主要用于获取图片大小并 * @param $file * @return array */ protected function getSize($file) { if (!file_exists($file)) return []; $info = pathinfo($file); $image = null; switch (strtolower($info['extension'])) { case 'gif': $image = imagecreatefromgif($file); break; case 'jpg': case 'jpeg': $image = imagecreatefromjpeg($file); break; case 'png': $image = imagecreatefrompng($file); break; default: break; } if ($image == null) return []; else return [imagesx($image), imagesy($image)]; } /** * 文件和图片管理action使用 * @param $allowFiles * @param $listSize * @param $path * @return array */ protected function manage($allowFiles, $listSize, $path) { $allowFiles = substr(str_replace('.', '|', join('', $allowFiles)), 1); /* 获取参数 */ $size = isset($_GET['size']) ? $_GET['size'] : $listSize; $start = isset($_GET['start']) ? $_GET['start'] : 0; $end = $start + $size; /* 获取文件列表 */ $path = $this->webroot . (substr($path, 0, 1) == '/' ? '' : '/') . $path; $files = $this->getFiles($path, $allowFiles); if (!count($files)) { $result = [ 'state' => 'no match file', 'list' => [], 'start' => $start, 'total' => count($files), ]; return $result; } /* 获取指定范围的列表 */ $len = count($files); for ($i = min($end, $len) - 1, $list = []; $i < $len && $i >= 0 && $i >= $start; $i--) { $list[] = $files[$i]; } /* 返回数据 */ $result = [ 'state' => 'SUCCESS', 'list' => $list, 'start' => $start, 'total' => count($files), ]; return $result; } /** * 遍历获取目录下的指定类型的文件 * @param $path * @param $allowFiles * @param array $files * @return array|null */ protected function getFiles($path, $allowFiles, &$files = []) { if (!is_dir($path)) return null; if (in_array(basename($path), $this->ignoreDir)) return null; if (substr($path, strlen($path) - 1) != '/') $path .= '/'; $handle = opendir($path); //baseUrl用于兼容使用alias的二级目录部署方式 $baseUrl = Yii::$app->request->baseUrl; while (false !== ($file = readdir($handle))) { if ($file != '.' && $file != '..') { $path2 = $path . $file; if (is_dir($path2)) { $this->getFiles($path2, $allowFiles, $files); } else { if ($this->action->id == 'list-image' && $this->thumbnail) { $pat = "/\_thumbnail\.(" . $allowFiles . ")$/i"; } else { $pat = "/\.(" . $allowFiles . ")$/i"; } if (preg_match($pat, $file)) { $files[] = [ 'url' => $baseUrl . substr($path2, strlen($this->webroot)), 'mtime' => filemtime($path2) ]; } } } } return $files; } /** * 最终显示结果,自动输出 JSONP 或者 JSON * @param array $result * @return array */ protected function show($result) { $callback = Yii::$app->request->get('callback', null); if ($callback && is_string($callback)) { Yii::$app->response->format = yii\web\Response::FORMAT_JSONP; return [ 'callback' => $callback, 'data' => $result ]; } Yii::$app->response->format = yii\web\Response::FORMAT_JSON; return $result; } }